Analyze Data
Decalarative SQL to interact with many data
Leverage query engine for data processing
Readable, maintainable, tranferable
Department of Energy’s EIA: Monthly Energy Review &
NOAA Mauna Loa, Hawaii Observatory - Carbon PPM
SELECT p.date_year, p.date_month, CONCAT(p.date_year, '-', p.date_month, '-01')::date as date_day,
p.average_ppm as "carbon ppm", c.energy_consumed as "btu consumed", e.energy_co2 as "co2 emissions"
FROM ppm_month p
JOIN consumption c
ON p.date_year = c.date_year AND p.date_month = c.date_month and c.msn = 'TXRCBUS'
JOIN us_co2_emissions e
ON p.date_year = e.date_year AND p.date_month = e.date_month and e.msn = 'TETCEUS'
ORDER BY p.date_year, p.date_month
SELECT p.date_year,
sum(p.average_ppm) as carbon_ppm_total,
avg(p.average_ppm) as carbon_ppm_mean,
sum(c.energy_consumed) as btu_consumed_total,
avg(c.energy_consumed) as btu_consumed_mean,
sum(e.energy_co2) as co2_emissions_total,
avg(e.energy_co2) as co2_emissions_mean
FROM ppm_month p
JOIN consumption c
ON p.date_year = c.date_year AND p.date_month = c.date_month and c.msn = 'TXRCBUS'
JOIN us_co2_emissions e
ON p.date_year = e.date_year AND p.date_month = e.date_month and e.msn = 'TETCEUS'
GROUP BY p.date_year
ORDER BY p.date_year
kable_styling(kable(tail(agg_df, 10)),
bootstrap_options = c("striped", "hover"))
| |
date_year |
carbon_ppm_total |
carbon_ppm_mean |
btu_consumed_total |
btu_consumed_mean |
co2_emissions_total |
co2_emissions_mean |
| 38 |
2010 |
4678.79 |
389.8992 |
6641.355 |
553.4462 |
5585.741 |
465.4784 |
| 39 |
2011 |
4699.83 |
391.6525 |
6473.666 |
539.4722 |
5446.133 |
453.8444 |
| 40 |
2012 |
4726.24 |
393.8533 |
5684.503 |
473.7086 |
5237.300 |
436.4417 |
| 41 |
2013 |
4758.25 |
396.5208 |
6689.368 |
557.4473 |
5363.018 |
446.9182 |
| 42 |
2014 |
4783.77 |
398.6475 |
7007.139 |
583.9283 |
5411.193 |
450.9327 |
| 43 |
2015 |
4810.01 |
400.8342 |
6465.092 |
538.7577 |
5264.776 |
438.7313 |
| 44 |
2016 |
4850.87 |
404.2392 |
6033.098 |
502.7582 |
5172.402 |
431.0335 |
| 45 |
2017 |
4878.64 |
406.5533 |
6111.580 |
509.2983 |
5130.589 |
427.5491 |
| 46 |
2018 |
4902.26 |
408.5217 |
6896.516 |
574.7097 |
5267.750 |
438.9792 |
| 47 |
2019 |
410.83 |
410.8300 |
1163.200 |
1163.2000 |
493.839 |
493.8390 |
metric_ts <- xts(x=metrics_df[c("carbon ppm", "btu consumed", "co2 emissions")],
order.by=metrics_df$date_day)
plot(metric_ts, main = "Energy and Carbon PPM Metrics",
legend.loc="bottomright",
col = seaborn_palette[1:3],
yaxis.right=FALSE,
axes=FALSE,
lwd=1,
cex.main=3,
major.ticks="years",
major.format="%Y",
minor.format="%Y",
grid.ticks.lty=1)

Seasonal Decomposition
carbonppm_ts <- ts(metrics_df$`carbon ppm`, start=c(1973, 1), frequency=12)
carbonppm_stl <- stl(carbonppm_ts, s.window="periodic")
plot(carbonppm_stl, col = seaborn_palette[1],
main="Seasonal Decomposition of Global Carbon PPM")

consumed_ts <- ts(metrics_df$`btu consumed`, start=c(1973, 1), frequency=12)
consumed_stl <- stl(consumed_ts, s.window="periodic")
plot(consumed_stl, col = seaborn_palette[2],
main="Seasonal Decomposition of U.S. Energy Consumption")

emissions_ts <- ts(metrics_df$`co2 emissions`, start=c(1973, 1), frequency=12)
emissions_stl <- stl(emissions_ts, s.window="periodic")
plot(emissions_stl, col = seaborn_palette[3],
main = "Seasonal Decomposition of U.S. CO2 Emissions")

sql <- "WITH c1 AS
(SELECT CONCAT((date_year/10)::int * 10, 's') as decade,
REPLACE(description, 'Primary Energy Consumed by the ', '') AS \"Sector\",
SUM(energy_consumed) AS \"Primary Energy Consumed\"
FROM consumption
WHERE msn IN ('TXICBUS', 'TXRCBUS', 'TXACBUS', 'TXCCBUS', 'TXEIBUS')
GROUP BY CONCAT((date_year/10)::int * 10, 's'), description
), c2 AS
(SELECT CONCAT((date_year/10)::int * 10, 's') as decade,
REPLACE(description, 'Total Energy Consumed by the ', '') AS \"Sector\",
SUM(energy_consumed) AS \"Total Energy Consumed\"
FROM consumption
WHERE msn IN ('TECCBUS', 'TEACBUS', 'TEICBUS', 'TERCBUS')
GROUP BY CONCAT((date_year/10)::int * 10, 's'), description
)
SELECT c1.decade, c1.\"Sector\", c1.\"Primary Energy Consumed\", c2.\"Total Energy Consumed\"
FROM c1
LEFT JOIN c2
ON c1.\"Sector\" = c2.\"Sector\" AND c1.decade = c2.decade
ORDER BY c1.decade, c1.\"Sector\"
"
consumed_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(consumed_df)),
bootstrap_options = c("striped", "hover"))
| |
decade |
Sector |
Primary Energy Consumed |
Total Energy Consumed |
| 35 |
2000s |
Transportation Sector |
546616.33 |
548112.4 |
| 36 |
2010s |
Commercial Sector |
77171.71 |
326503.7 |
| 37 |
2010s |
Electric Power Sector |
693591.66 |
NA |
| 38 |
2010s |
Industrial Sector |
388661.21 |
570178.3 |
| 39 |
2010s |
Residential Sector |
117157.95 |
378492.5 |
| 40 |
2010s |
Transportation Sector |
490149.36 |
491557.4 |
Consumption and CO2 Emissions
plot_mat <- with(subset(consumed_df, Sector != 'Electric Power Sector'),
tapply(`Total Energy Consumed`, list(decade, `Sector`), sum))
par(mar=c(5, 5, 2, 1))
barplot(plot_mat, main="U.S. Total Energy Consumption by Sector", cex.main=1.5,
col=seaborn_palette[1:8], ylim=c(0, 8E5), xaxt="n", yaxt="n", beside=TRUE)
axis(side=1, at=c(5, 14, 23, 32), labels=colnames(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:8], ncol=8)

plot_mat <- with(consumed_df, tapply(`Primary Energy Consumed`, list(decade, `Sector`), sum))
par(mar=c(5, 5, 2, 1))
barplot(plot_mat, main="U.S. Primary Energy Consumption by Sector", cex.main=1.5,
col=seaborn_palette[1:8], ylim=c(0, 9E5), xaxt="n", yaxt="n", beside=TRUE)
axis(side=1, at=c(5, 14, 23, 32, 41), labels=colnames(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:8], ncol=8)

sql <- "SELECT CONCAT((date_year/10)::int * 10, 's') as decade,
REPLACE(REPLACE(description, 'Total Energy ', ''), ' CO2 Emissions', '') AS \"Sector\",
SUM(energy_co2) AS \"Total CO2 Emissions\"
FROM us_co2_emissions
WHERE msn IN ('TERCEUS', 'TECCEUS', 'TEACEUS', 'TXEIEUS')
GROUP BY date_year, description
ORDER BY date_year, description
"
emissions_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(emissions_df)),
bootstrap_options = c("striped", "hover"))
| |
decade |
Sector |
Total CO2 Emissions |
| 183 |
2010s |
Residential Sector |
2034.691 |
| 184 |
2010s |
Transportation Sector |
3830.401 |
| 185 |
2010s |
Commercial Sector |
164.804 |
| 186 |
2010s |
Electric Power Sector |
274.988 |
| 187 |
2010s |
Residential Sector |
223.165 |
| 188 |
2010s |
Transportation Sector |
295.360 |
plot_mat <- with(emissions_df, tapply(`Total CO2 Emissions`, list(decade, `Sector`), sum))
par(mar=c(5, 8, 2, 1))
barplot(plot_mat, main="U.S. CO2 Emissions by Sector", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 6E4), xaxt="n", yaxt="n", beside=TRUE)
axis(side=1, at=c(3, 9, 15, 21)+0.5, labels=colnames(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

sql <- "WITH sub AS
(SELECT CONCAT((date_year/10)::int * 10, 's') as decade,
REPLACE($1, '%', '') as sector,
REPLACE(
REPLACE(
REPLACE(
REPLACE(description, 'Commercial Sector CO2 Emissions', ''),
'Residential Sector CO2 Emissions', ''
), 'Transportation Sector CO2 Emissions', ''),
' ', '\n') AS \"Type\",
energy_co2
FROM us_co2_emissions
WHERE description LIKE $2)
SELECT decade, sector, \"Type\",
SUM(energy_co2) AS \"Total CO2 Emissions\"
FROM sub
GROUP BY decade, sector, \"Type\"
ORDER BY decade, sector, \"Type\"
"
params <- paste0(c("%Transportation", "%Residential", "%Commercial"), " Sector%")
emissions_type_df <- do.call(rbind, lapply(params, function(p) dbGetQuery(conn, sql, param=list(p, p))))
par(mfrow=c(3,2), mar=c(5, 5, 2, 1), mai = c(0.7, 0.2, 0.7, 0.2))
output <- by(emissions_type_df, emissions_type_df$sector, function(sub) {
plot_mat <- with(sub, tapply(`Total CO2 Emissions`, list(decade, `Type`), sum))
barplot(plot_mat, main=paste("U.S. CO2 Emissions by", sub$sector[[1]]), cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, ceiling(max(plot_mat, na.rm=TRUE)/1E4) * 1E4),
xaxt="n", yaxt="n", beside=TRUE)
axis(side=1, at=c(3,9,15,21,27,33,39,45,51,57,63)[seq_along(colnames(plot_mat))] + 0.5,
labels=colnames(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)
})

Renewable Energy: Production and Consumption
sql <- "SELECT energy_type,
date,
SUM(production) AS production,
SUM(consumption) AS consumption
FROM us_renewable_energy
GROUP BY energy_type,
date
ORDER BY energy_type,
date
"
renewable_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(renewable_df)),
bootstrap_options = c("striped", "hover"))
| |
energy_type |
date |
production |
consumption |
| 4678 |
Wood Energy |
2019-02-01 |
190.887 |
182.491 |
| 4679 |
Wood Energy |
2019-03-01 |
198.621 |
191.507 |
| 4680 |
Wood Energy |
2019-04-01 |
195.791 |
187.670 |
| 4681 |
Wood Energy |
2019-05-01 |
201.743 |
193.775 |
| 4682 |
Wood Energy |
2019-06-01 |
198.379 |
189.036 |
| 4683 |
Wood Energy |
2019-07-01 |
205.023 |
196.873 |
par(mfrow=c(3,3), mar=c(5, 5, 2, 1))
output <- by(renewable_df, renewable_df$energy_type, function(sub) {
metric_ts <- xts(x=sub[c("production", "consumption")],
order.by=sub$date)
print(plot(metric_ts, main = sub$energy_type[1],
legend.loc="bottomright",
col = seaborn_palette[1:3],
yaxis.right=FALSE,
axes=FALSE,
lwd=1,
cex.main=3,
major.ticks="years",
major.format="%Y",
minor.format="%Y",
grid.ticks.lty=1))
})

U.S. Geological Survey - Groundwater Well Depth Data
sql <- "SELECT g.year, g.month,
avg(g.mean_value) as mean_value
FROM groundwater g
WHERE g.year BETWEEN 1990 AND 2019
GROUP BY g.year, g.month"
groundwater_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(groundwater_df)),
bootstrap_options = c("striped", "hover"))
| |
year |
month |
mean_value |
| 350 |
2019 |
2 |
63735.69 |
| 351 |
2019 |
3 |
60792.13 |
| 352 |
2019 |
4 |
54698.44 |
| 353 |
2019 |
5 |
53237.05 |
| 354 |
2019 |
6 |
49160.99 |
| 355 |
2019 |
7 |
76941.00 |
boxplot(mean_value ~ year, groundwater_df, col=seaborn_palette[1:10],
main="Groundwater Well Depth Mean Values", cex.main=1.5)

sql <- "WITH sites AS (
SELECT CONCAT(g.year, '-', g.month, '-1')::date AS \"date\",
g.site_name,
AVG(g.mean_value) as mean_value
FROM groundwater g
WHERE g.year BETWEEN 1990 AND 2019
GROUP BY CONCAT(g.year, '-', g.month, '-1')::date,
g.site_name
), bottom AS (
SELECT site_name, AVG(mean_value) AS mean_value, 'bottom_sites' AS category
FROM sites
GROUP BY site_name
ORDER BY AVG(mean_value) ASC LIMIT 5
), top AS (
SELECT site_name, AVG(mean_value) AS mean_value, 'top_sites' AS category
FROM sites
GROUP BY site_name
ORDER BY AVG(mean_value) DESC LIMIT 5
)
SELECT sites.\"date\", sites.mean_value, sites.site_name, top.category
FROM sites
INNER JOIN top
ON sites.site_name = top.site_name
UNION
SELECT sites.\"date\", sites.mean_value, sites.site_name, bottom.category
FROM sites
INNER JOIN bottom
ON sites.site_name = bottom.site_name
ORDER BY category, site_name, \"date\""
groundwater_df <- dbGetQuery(conn, sql)
kable_styling(kable(head(groundwater_df)),
bootstrap_options = c("striped", "hover"))
| date |
mean_value |
site_name |
category |
| 1991-06-01 |
31 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-07-01 |
35 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-08-01 |
41 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-09-01 |
42 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-10-01 |
36 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-11-01 |
40 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
groundwater_df$year <- format(groundwater_df$date, "%Y")
par(mfrow=c(5,2), mar=c(5, 5, 2, 1))
output <- by(groundwater_df, groundwater_df$site_name, function(sub) {
metric_ts <- xts(x=sub[c("mean_value")],
order.by=sub$date)
print(plot(metric_ts, main = paste(sub$site_name[1], ':', sub$category[1]),
legend.loc="bottomright",
col = seaborn_palette[1:3],
yaxis.right=FALSE,
axes=FALSE,
lwd=1,
cex.main=3,
major.ticks="years",
major.format="%Y",
minor.format="%Y",
grid.ticks.lty=1))
})

U.S. FWS Endangered Species List
sql <- "SELECT CONCAT((f.date_year/10)::int * 10, 's') AS decade,
f.taxonomic_group,
f.current_status,
count(*) AS species_count
FROM fws_species_year f
WHERE f.current_status IN ('Endangered', 'Recovery', 'Resolved Taxon',
'Threatened', 'Extinction')
GROUP BY CONCAT((f.date_year/10)::int * 10, 's'),
f.taxonomic_group,
f.current_status
ORDER BY CONCAT((f.date_year/10)::int * 10, 's'),
f.taxonomic_group,
f.current_status"
fws_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(fws_df)),
bootstrap_options = c("striped", "hover"))
| |
decade |
taxonomic_group |
current_status |
species_count |
| 141 |
2010s |
Mammals |
Resolved Taxon |
1 |
| 142 |
2010s |
Mammals |
Threatened |
11 |
| 143 |
2010s |
Reptiles |
Endangered |
4 |
| 144 |
2010s |
Reptiles |
Threatened |
9 |
| 145 |
2010s |
Snails |
Endangered |
18 |
| 146 |
2010s |
Snails |
Threatened |
1 |
par(mfrow=c(5,3), mar=c(5, 5, 2, 1))
output <- by(fws_df, fws_df$taxonomic_group, function(sub) {
plot_mat <- with(sub, tapply(species_count, list(decade, current_status), sum))
barplot(plot_mat, main=sub$taxonomic_group[[1]], cex.main=1.5,
ylim = c(0, max(plot_mat, na.rm=TRUE)+5),
col=seaborn_palette[seq_along(row.names(plot_mat))], beside=TRUE)
legend("top", row.names(plot_mat), fill=seaborn_palette[seq_along(row.names(plot_mat))],
ncol=length(row.names(plot_mat)))
box()
})

U.S. Department of Agriculture: Agriculture Census
sql <- "SELECT year,
CASE
WHEN domain_category = '' THEN 'TOTAL\nOPERATIONS'
ELSE REPLACE(REPLACE(REPLACE(domain_category, '(', '\n('), 'TO', 'TO\n'), 'OR', 'OR\n')
END AS domain_category,
value
FROM ag_census
WHERE data_item = 'FARM OPERATIONS - NUMBER OF OPERATIONS'"
agcensus_df <- dbGetQuery(conn, sql)
plot_mat <- with(agcensus_df, tapply(value, list(year, domain_category), sum))
par(mar=c(5, 5, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Number of Farm Operations", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 2E6+5E5), beside=TRUE, xaxt="n", yaxt="n")
axis(side=1, at=c(3,9,15,21,27,33,39,45)+0.5, labels=colnames(plot_mat), tick=FALSE, padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

sql <- "WITH sub AS (
SELECT year,
data_item,
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(domain_category, ';', ','),
'(', '\n('),
'TO', 'TO\n'),
' OR', ' OR\n'),
'LESS', 'LESS\n') AS domain_category,
value
FROM ag_census
WHERE data_item LIKE '%COMMODITY TOTALS%'
AND value IS NOT NULL
)
SELECT year,
CASE
WHEN TRIM(domain_category) = ''
THEN CASE
WHEN data_item = 'COMMODITY TOTALS - SALES; MEASURED IN $'
THEN 'TOTAL\nSALES'
WHEN data_item = 'COMMODITY TOTALS - SALES; MEASURED IN $ / OPERATION'
THEN 'TOTAL\nSALES\nPER OPERATION'
END
ELSE domain_category
END AS domain_category,
value
FROM sub
"
agcensus_df <- within(dbGetQuery(conn, sql),
domain_category <- factor(domain_category,
levels = c("FARM SALES: \n(LESS\n THAN 2,500 $)", "FARM SALES: \n(2,500 TO\n 4,999 $)",
"FARM SALES: \n(5,000 TO\n 9,999 $)", "FARM SALES: \n(10,000 TO\n 24,999 $)",
"FARM SALES: \n(25,000 TO\n 49,999 $)", "FARM SALES: \n(50,000 TO\n 99,999 $)",
"FARM SALES: \n(100,000 TO\n 499,999 $)", "FARM SALES: \n(500,000 OR\n MORE $)",
"TOTAL\nSALES", "TOTAL\nSALES\nPER OPERATION"))
)
kable_styling(kable(tail(agcensus_df)),
bootstrap_options = c("striped", "hover"))
| |
year |
domain_category |
value |
| 45 |
2017 |
FARM SALES:
(5,000 TO
9,999 $) |
208074 |
| 46 |
2017 |
FARM SALES:
(10,000 TO
24,999 $) |
228218 |
| 47 |
2017 |
FARM SALES:
(25,000 TO
49,999 $) |
144113 |
| 48 |
2017 |
FARM SALES:
(50,000 TO
99,999 $) |
119434 |
| 49 |
2017 |
FARM SALES:
(100,000 TO
499,999 $) |
218771 |
| 50 |
2017 |
FARM SALES:
(500,000 OR
MORE $) |
146568 |
plot_mat <- with(agcensus_df[agcensus_df$domain_category != 'TOTAL\nSALES',],
tapply(value, list(year, factor(domain_category)), sum))
par(mar=c(5, 5, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Commodity Totals - Farm Sales", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 1E6), beside=TRUE, xaxt="n", yaxt="n")
axis(side=1, at=c(3,9,15,21,27,33,39,45,51)+0.5, labels=colnames(plot_mat), tick=FALSE, padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

plot_mat <- with(agcensus_df[agcensus_df$domain_category == 'TOTAL\nSALES',],
tapply(value, list(year, factor(domain_category)), sum))
par(mar=c(5, 8, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Commodity Totals - Overall Farm Sales", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 4E11+5E10), beside=TRUE, xaxt="n", yaxt="n", space=2)
axis(side=1, at=c(2.5,5.5,8.5,11.5,14.5), labels=row.names(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

sql <- "SELECT year,
REPLACE(REPLACE(data_item, ' - OPERATIONS WITH AREA HARVESTED', ''), '; GRAIN', '') as crop,
value
FROM ag_census
WHERE data_item IN ('CORN; GRAIN - OPERATIONS WITH AREA HARVESTED',
'WHEAT - OPERATIONS WITH AREA HARVESTED',
'SOYBEANS - OPERATIONS WITH AREA HARVESTED')
ORDER BY year, data_item
"
crops_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(crops_df)),
bootstrap_options = c("striped", "hover"))
| |
year |
crop |
value |
| 10 |
2012 |
CORN |
348530 |
| 11 |
2012 |
SOYBEANS |
302963 |
| 12 |
2012 |
WHEAT |
147632 |
| 13 |
2017 |
CORN |
304801 |
| 14 |
2017 |
SOYBEANS |
303191 |
| 15 |
2017 |
WHEAT |
104792 |
plot_mat <- with(crops_df, tapply(value,
list(year, factor(crop, levels=c("WHEAT", "SOYBEANS", "CORN"))), sum)
)
par(mar=c(5, 8, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Number of Operations by Specific Crops", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 5E5), yaxt="n", beside=TRUE)
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

sql <- "SELECT year,
REPLACE(
REPLACE(
REPLACE(data_item, ' - OPERATIONS WITH SALES', ''),
'; INCL CALVES', ''),
'; BROILERS', '') as livestock,
value
FROM ag_census
WHERE data_item IN ('CATTLE; INCL CALVES - OPERATIONS WITH SALES',
'HOGS - OPERATIONS WITH SALES',
'CHICKENS; BROILERS - OPERATIONS WITH SALES')
ORDER BY year, data_item
"
livestock_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(livestock_df)),
bootstrap_options = c("striped", "hover"))
| |
year |
livestock |
value |
| 10 |
2012 |
CATTLE |
740978 |
| 11 |
2012 |
CHICKENS |
32935 |
| 12 |
2012 |
HOGS |
55882 |
| 13 |
2017 |
CATTLE |
711827 |
| 14 |
2017 |
CHICKENS |
32751 |
| 15 |
2017 |
HOGS |
64871 |
plot_mat <- with(livestock_df, tapply(value,
list(year, factor(livestock, levels=c( "CHICKENS", "HOGS", "CATTLE"))), sum)
)
par(mar=c(5, 8, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Number of Operations by Specific Livestock", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 1E6+2E5), yaxt="n", beside=TRUE)
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

sql <- "SELECT year,
CASE
WHEN data_item = 'CROP TOTALS - SALES; MEASURED IN $' THEN 'CROP TOTALS'
WHEN data_item = 'ANIMAL TOTALS; INCL PRODUCTS - SALES; MEASURED IN $' THEN 'ANIMAL TOTALS'
END as category,
value
FROM ag_census
WHERE data_item IN ('CROP TOTALS - SALES; MEASURED IN $',
'ANIMAL TOTALS; INCL PRODUCTS - SALES; MEASURED IN $')
ORDER BY year, data_item
"
agtotal_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(agtotal_df)),
bootstrap_options = c("striped", "hover"))
| |
year |
category |
value |
| 5 |
2007 |
ANIMAL TOTALS |
153562563000 |
| 6 |
2007 |
CROP TOTALS |
143657928000 |
| 7 |
2012 |
ANIMAL TOTALS |
182247407000 |
| 8 |
2012 |
CROP TOTALS |
212397074000 |
| 9 |
2017 |
ANIMAL TOTALS |
194975996000 |
| 10 |
2017 |
CROP TOTALS |
193546699000 |
plot_mat <- with(agtotal_df, tapply(value, list(year, category), sum))
par(mar=c(5, 8, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Crop vs Animal Sales", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 2E11+5E10), yaxt="n", beside=TRUE)
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

World Metrics
sql <- "WITH pop AS
(SELECT p.year,
p.population
FROM world_population p
WHERE p.country_name = 'World'
AND p.year BETWEEN 2000 AND 2019
),
land AS
(SELECT a.year,
a.percent_arable
FROM arable_land a
WHERE a.country_name = 'World'
AND a.year BETWEEN 2000 AND 2019
),
fauna AS
(SELECT i.year,
SUM(i.species_count) AS animals_count
FROM iucn_species_count i
WHERE i.year BETWEEN 2000 AND 2019
GROUP BY i.year
),
flora AS
(SELECT p.assessment_year as year,
COUNT(*) AS plants_count
FROM plants_assessments p
WHERE p.assessment_year BETWEEN 2000 AND 2019
AND p.interpreted_status = 'Threatened'
GROUP BY p.assessment_year
),
ice AS
(SELECT s.date_year as year,
AVG(s.extent) FILTER(WHERE s.region = 'Arctic') AS arctic_sea_ice_extent,
AVG(s.extent) FILTER(WHERE s.region = 'Antarctica') AS antarctic_sea_ice_extent
FROM sea_ice_extent s
WHERE s.date_year BETWEEN 2000 AND 2019
GROUP BY s.date_year
),
ocean AS
(SELECT o.year as year,
AVG(o.tco2) AS total_co2,
AVG(o.phts25p0) AS ph_scale
FROM ocean_data o
WHERE o.year BETWEEN 2000 AND 2019
AND o.tco2 <> -9999 AND o.phts25p0 <> -9999
GROUP BY o.year
),
temp AS
(SELECT g.year as year,
AVG(g.global_mean) AS global_mean
FROM global_temperature g
WHERE g.year BETWEEN 2000 AND 2019
GROUP BY g.year
)
SELECT pop.year, pop.population, land.percent_arable, fauna.animals_count, flora.plants_count,
ice.arctic_sea_ice_extent, ice.antarctic_sea_ice_extent,
ocean.total_co2, ocean.ph_scale, temp.global_mean
FROM pop
JOIN land USING (year)
JOIN fauna USING (year)
JOIN flora USING (year)
JOIN ice USING (year)
JOIN ocean USING (year)
JOIN temp USING (year)
ORDER BY pop.year"
env_world_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(env_world_df)), font_size = 12,
bootstrap_options = c("striped", "hover"))
| |
year |
population |
percent_arable |
animals_count |
plants_count |
arctic_sea_ice_extent |
antarctic_sea_ice_extent |
total_co2 |
ph_scale |
global_mean |
| 7 |
2008 |
6765986891 |
10.80640 |
16928 |
3279 |
10.97785 |
12.23941 |
2181.326 |
7.685694 |
0.5155556 |
| 8 |
2009 |
6849272706 |
10.77478 |
17291 |
2294 |
10.93197 |
12.04859 |
2176.104 |
7.735574 |
0.6294444 |
| 9 |
2010 |
6932596129 |
10.74432 |
18351 |
3217 |
10.71139 |
12.10679 |
2191.072 |
7.702093 |
0.7022222 |
| 10 |
2011 |
7014792135 |
10.80189 |
19570 |
2850 |
10.48350 |
11.50057 |
2224.690 |
7.675097 |
0.5844444 |
| 11 |
2012 |
7099311892 |
10.87358 |
20219 |
7556 |
10.40610 |
12.00444 |
2187.112 |
7.731700 |
0.6183333 |
| 12 |
2013 |
7184861447 |
10.89469 |
21353 |
5465 |
10.89712 |
12.52361 |
2183.400 |
7.723483 |
0.6438889 |
kable_styling(kable(cor(env_world_df[-1])), font_size = 12,
bootstrap_options = c("striped", "hover"))
| |
population |
percent_arable |
animals_count |
plants_count |
arctic_sea_ice_extent |
antarctic_sea_ice_extent |
total_co2 |
ph_scale |
global_mean |
| population |
1.0000000 |
0.1656147 |
0.9774118 |
0.7453433 |
-0.8026257 |
0.5047082 |
0.5832658 |
-0.1705909 |
0.5630321 |
| percent_arable |
0.1656147 |
1.0000000 |
0.2441270 |
0.4917138 |
0.0689147 |
0.3901610 |
-0.0431136 |
0.1567368 |
-0.3399306 |
| animals_count |
0.9774118 |
0.2441270 |
1.0000000 |
0.7450838 |
-0.8024194 |
0.5181381 |
0.6740957 |
-0.2912376 |
0.4783654 |
| plants_count |
0.7453433 |
0.4917138 |
0.7450838 |
1.0000000 |
-0.7224389 |
0.3576609 |
0.3673134 |
-0.1134181 |
0.3630865 |
| arctic_sea_ice_extent |
-0.8026257 |
0.0689147 |
-0.8024194 |
-0.7224389 |
1.0000000 |
-0.0588615 |
-0.7926131 |
0.5220906 |
-0.5461557 |
| antarctic_sea_ice_extent |
0.5047082 |
0.3901610 |
0.5181381 |
0.3576609 |
-0.0588615 |
1.0000000 |
0.1042700 |
0.0767117 |
0.0713994 |
| total_co2 |
0.5832658 |
-0.0431136 |
0.6740957 |
0.3673134 |
-0.7926131 |
0.1042700 |
1.0000000 |
-0.8165837 |
0.2653096 |
| ph_scale |
-0.1705909 |
0.1567368 |
-0.2912376 |
-0.1134181 |
0.5220906 |
0.0767117 |
-0.8165837 |
1.0000000 |
0.0463203 |
| global_mean |
0.5630321 |
-0.3399306 |
0.4783654 |
0.3630865 |
-0.5461557 |
0.0713994 |
0.2653096 |
0.0463203 |
1.0000000 |
par(mfrow=c(4,2), mai = c(0.3, 0.2, 0.7, 0.2))
for(x in colnames(env_world_df)[3:ncol(env_world_df)]) {
lfit <- loess(paste(x, "~ population"), data=env_world_df)
plot(as.formula(paste(x, "~ population")), env_world_df,
main=paste("population and\n", gsub("_", " ", x)), cex.main=2,
type="p", col=seaborn_palette[1], yaxt='n', xaxt='n', pch=19)
axis(side=1, at=axTicks(1), labels=format(axTicks(1), big.mark=',', scientific=FALSE))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE))
pop_order <- order(env_world_df$population)
lines(env_world_df$population[pop_order], lfit$fitted[pop_order], col=seaborn_palette[4], lwd=3)
}

dbDisconnect(conn)
[1] TRUE
User Data Application
# LINUX SHELL COMMAND CALL
system(paste0("gnome-terminal -- Rscript -e \"library(shiny); setwd('", getwd(), "'); runApp('EnvironmentDB_Shiny_App.R')\""))
Conclusion
Postgres as tool in data science workflow
Data persistence and hygiene
Centralization and security
Efficiency and usefulness
Scalability and applicability
LS0tCnRpdGxlOiAiUG9zdGdyZVNRTCBBcyBEYXRhIFNjaWVuY2UgRGF0YWJhc2UiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Ci5tYWluLWNvbnRhaW5lciB7CiAgbWF4LXdpZHRoOiAxMDAwcHg7CiAgbWFyZ2luLWxlZnQ6IGF1dG87CiAgbWFyZ2luLXJpZ2h0OiBhdXRvOwp9Cjwvc3R5bGU+Cgo8YnIvPgo8ZGl2IHN0eWxlPSJmb250LXNpemU6IDIwcHg7Ij5DaGljYWdvIFBvc3RncmVzIFVzZXIgR3JvdXAgTWVldHVwLCBKYW51YXJ5IDEzLCAyMDIwPC9kaXY+CjxkaXYgc3R5bGU9ImZsb2F0OmxlZnQiPjxpbWcgc3JjPSJJTUFHRVMvcG9zdGdyZXNxbF9yLnBuZyIgd2lkdGg9IjIwMHB4Ii8+PC9kaXY+Cjxici8+PGJyLz48YnIvPjxici8+CgojIyBQYXJmYWl0IEdhc2FuYSAjIwo8ZGl2IHN0eWxlPSJmb250LXNpemU6IDIwcHg7Ij5EYXRhIEFuYWx5c3QsIFdpbnN0b24gJiBTdHJhd248L2Rpdj4KPGRpdiBzdHlsZT0iZmxvYXQ6bGVmdCI+PGltZyBzcmM9IklNQUdFUy9zdGFja292ZXJmbG93LnBuZyIgd2lkdGg9IjMwcHgiLz48L2Rpdj4KPGRpdiBzdHlsZT0iZm9udC1zaXplOiAxNnB4OyI+QFBhcmZhaXQgKFN0YWNrT3ZlcmZsb3cpIHwgCjxpbWcgc3JjPSJJTUFHRVMvZ2l0aHViLnBuZyIgd2lkdGg9IjMwcHgiLz4mbmJzcDsmbmJzcDtAUGFyZmFpdEcgKEdpdEh1Yik8L2Rpdj4KCjxociBzdHlsZT0iYm9yZGVyOiBub25lOyBoZWlnaHQ6IDFweDsgYmFja2dyb3VuZC1jb2xvcjogI0NDQzsiLz4KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQocm9vdC5kaXIgPSAiL2hvbWUvcGFyZmFpdGcvRG9jdW1lbnRzL1BHU1FMIikKYGBgCgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGltZyBzcmM9IklNQUdFUy9vcGVuX3NvdXJjZS5wbmciIHdpZHRoPSI3NXB4Ii8+PC9kaXY+Cjxici8+CgojIyBPcGVuIFNvdXJjZSBTdWNjZXNzIFN0b3JpZXMKCjxici8+Cgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGltZyBzcmM9IklNQUdFUy9wb3N0Z3Jlc3FsLmpwZWciIHdpZHRoPSI1MHB4Ii8+PC9kaXY+Ci0gIyMjIFBvc3RncmVTUUw6IHBvd2VyZnVsLCBleHRlbnNpYmxlLCBhbmFseXRpY2FsIFJEQk1TCiAgICAtICMjIyMgW0NvbW11bml0eV0oaHR0cHM6Ly93d3cucG9zdGdyZXNxbC5vcmcvY29tbXVuaXR5Lyk6IGNvbnRyaWJ1dG9ycywgbWFpbGluZyBsaXN0cywgbG9jYWwgdXNlciBncm91cHMKPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQiPjxpbWcgc3JjPSJJTUFHRVMvcl9sb2dvLnBuZyIgd2lkdGg9IjUwcHgiLz48L2Rpdj4KLSAjIyMgUjogZGF0YSBzY2llbmNlIHN0YWNrLCBleHRlbnNpYmxlIGVudmlyb25tZW50CiAgICAtICMjIyMgW0NSQU5dKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnLykgcmVwb3NpdG9yeSBvZiBwYWNrYWdlcwo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGltZyBzcmM9IklNQUdFUy9weXRob24ucG5nIiB3aWR0aD0iNTBweCIvPjwvZGl2PgotICMjIyBQeXRob246IGdlbmVyYWwgcHVycG9zZSwgZXh0ZW5zaWJsZSBsYW5ndWFnZQogICAgLSAjIyMjIFtQeVBJXShodHRwczovL3B5cGkub3JnLykgZWNvc3lzdGVtIG9mIG1vZHVsZXMKCjxkaXYgc3R5bGU9Im1hcmdpbi1sZWZ0OjQwcHg7Ii8+PGgzPkNoYWxsZW5naW5nIENvbW1lcmNpYWwgTWFya2V0IFNoYXJlPC9oMz48L2Rpdj4KPGRpdiBzdHlsZT0iZmxvYXQ6bGVmdDsgbWFyZ2luLWxlZnQ6MTAwcHg7Ii8+PGltZyBzcmM9IklNQUdFUy9jb21tZXJjaWFsX3Rvb2xzLnBuZyIgd2lkdGg9IjIwMHB4Ii8+PC9kaXY+Cjxici8+Cjxici8+Cjxici8+Cjxici8+Cgo8aHIgc3R5bGU9ImJvcmRlcjogbm9uZTsgaGVpZ2h0OiAxcHg7IGJhY2tncm91bmQtY29sb3I6ICNDQ0M7Ii8+Cgo8YnIvPgoKPGgyPjxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPlJlbGF0aW9uYWwgRGF0YWJhc2UgU29sdXRpb248L3NwYW4+PC9oMj4KCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0OyI+PGltZyBzcmM9IklNQUdFUy9SREJNU19Hcm91cC5wbmciIHdpZHRoPSIyNTBweCIvPjwvZGl2PgoKLSAjIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+RGF0YSBwZXJzaXN0ZW5jZTogaGlzdG9yaWNhbCBhbmQgY3VycmVudCBuZWVkczwvc3Bhbj4KLSAjIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+TWFpbnRlbmFuY2UvaHlnaWVuZTogYWRoZXJlbmNlIHRvIHR5cGVzPC9zcGFuPgoKPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQ7Ij48aW1nIHNyYz0iSU1BR0VTL3Bvc3RncmVzcWwuanBlZyIgd2lkdGg9IjEwMHB4Ii8+PC9kaXY+CgotICMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5TdG9yYWdlIGVmZmljaWVuY3k6IG5vcm1hbGl6YXRpb24gcmVkdWNlcyByZXBldGl0aW9uIG9mIGRhdGE8L3NwYW4+Ci0gIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPkNlbnRyYWxpemF0aW9uOiBtdWx0aXBsZSB1c2VyIGVudmlyb25tZW50IGFuZCBzZWN1cml0eTwvc3Bhbj4KLSAjIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+U2NhbGFiaWxpdHk6IG5vdCBsaW1pdGVkIHRvIGxvY2FsIHJlc291cmNlczwvc3Bhbj4KCjxociBzdHlsZT0iYm9yZGVyOiBub25lOyBoZWlnaHQ6IDFweDsgYmFja2dyb3VuZC1jb2xvcjogI0NDQzsiLz4KCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0Ij48aW1nIHNyYz0iSU1BR0VTL2RvY2tlcl9wZ3NxbC5wbmciIHdpZHRoPSIxNTBweCIvPjwvZGl2PgojIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5Vc2UgQ2FzZTogRW52aXJvbm1lbnQgRGF0YWJhc2U8L2E+PC9zcGFuPgoKPGgzPjxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPjxpPkh1bWFuIEltcGFjdCBvbiB0aGUgR2xvYmFsIGFuZCBMb2NhbCBCaW9zcGhlcmUgYW5kIENsaW1hdGU8L2k+PC9zcGFuPjwvaDM+CjxkaXYgc3R5bGU9ImZsb2F0OmNlbnRlcjsgdGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0iSU1BR0VTL2Vudl9kYXRhLnBuZyIvPjwvZGl2Pgo8YnIvPgoKPGhyIHN0eWxlPSJib3JkZXI6IG5vbmU7IGhlaWdodDogMXB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjQ0NDOyIvPgoKYGBge3J9CnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShrYWJsZUV4dHJhKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHh0cykpCgpzZWFib3JuX3BhbGV0dGUgPC0gYygiIzRDNzJCMCIsICIjREQ4NDUyIiwgIiM1NUE4NjgiLCAiI0M0NEU1MiIsICIjODE3MkIzIiwgIiM5Mzc4NjAiLCAKICAgICAgICAgICAgICAgICAgICAgIiNEQThCQzMiLCAiIzhDOEM4QyIsICIjQ0NCOTc0IiwgIiM2NEI1Q0QiLCAiIzRDNzJCMCIsICIjREQ4NDUyIikKYGBgCgojIyBDb25uZWN0aW9uCgotICMjIyBDZW50cmFsaXplZCBsb2NhdGlvbiBmb3IgbXVsdGlwbGUgdXNlcnMKLSAjIyMgV2VsbCBtYWludGFpbmVkIEFQSSBtb2R1bGVzCi0gIyMjIE11bHRpcGxlIGJhY2tlbmQgY29ubmVjdGlvbiB0eXBlcwoKIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPjxhIGhyZWY9Imh0dHBzOi8vd3d3LnBvc3RncmVzcWwub3JnL2Z0cC9vZGJjL3ZlcnNpb25zL3NyYy8iIHRhcmdldD0iYmxhbmsiPk9EQkM8L2E+PC9zcGFuPgoKYGBge3J9CmxpYnJhcnkoREJJKQpsaWJyYXJ5KG9kYmMpCgpjb25uIDwtIGRiQ29ubmVjdChvZGJjOjpvZGJjKCksIGRyaXZlcj0iUG9zdGdyZVNRTCBVbmljb2RlIiwgCiAgICAgICAgICAgICAgICAgIHNlcnZlcj0ibG9jYWxob3N0IiwgZGF0YWJhc2U9ImVudmlyb25tZW50IiwKICAgICAgICAgICAgICAgICAgdWlkPSJwb3N0Z3JlcyIsIHB3ZD0iZW52MTkiLCBwb3J0PTY0MzIpCmRiTGlzdFRhYmxlcyhjb25uKQoKZGJEaXNjb25uZWN0KGNvbm4pCmBgYAoKIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPjxhIGhyZWY9Imh0dHBzOi8vamRiYy5wb3N0Z3Jlc3FsLm9yZy9kb3dubG9hZC5odG1sIiB0YXJnZXQ9ImJsYW5rIj5KREJDPC9hPjwvc3Bhbj4KCmBgYHtyfQpsaWJyYXJ5KHJKYXZhKQpsaWJyYXJ5KFJKREJDKQoKZHJ2IDwtIEpEQkMoIm9yZy5wb3N0Z3Jlc3FsLkRyaXZlciIsCiAgICAgICAgICAgIi91c3IvbGliL2p2bS9qYXZhLTgtb3JhY2xlL2xpYi9wb3N0Z3Jlc3FsLTQyLjIuMi5qYXIiKQpjb25uIDwtIGRiQ29ubmVjdChkcnYsICJqZGJjOnBvc3RncmVzcWw6Ly9sb2NhbGhvc3Q6NjQzMi9lbnZpcm9ubWVudCIsICJwb3N0Z3JlcyIsICJlbnYxOSIpCmRiTGlzdFRhYmxlcyhjb25uLCBzY2hlbWE9InB1YmxpYyIpCgpkYkRpc2Nvbm5lY3QoY29ubikKYGBgCgoKIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPjxhIGhyZWY9Imh0dHBzOi8vcmRyci5pby9jcmFuL1JQb3N0Z3JlU1FMLyIgdGFyZ2V0PSJibGFuayI+Ui1Qb3N0Z3JlcyBBUEk8L2E+PC9zcGFuPgoKYGBge3J9CmxpYnJhcnkoUlBvc3RncmVTUUwpCgpjb25uIDwtIGRiQ29ubmVjdChSUG9zdGdyZVNRTDo6UG9zdGdyZVNRTCgpLCBob3N0PSJsb2NhbGhvc3QiLCBkYm5hbWU9ImVudmlyb25tZW50IiwKICAgICAgICAgICAgICAgICAgdXNlcj0icG9zdGdyZXMiLCBwYXNzd29yZD0iZW52MTkiLCBwb3J0PTY0MzIpCmRiTGlzdFRhYmxlcyhjb25uKQpgYGAKCjxociBzdHlsZT0iYm9yZGVyOiBub25lOyBoZWlnaHQ6IDFweDsgYmFja2dyb3VuZC1jb2xvcjogI0NDQzsiLz4KCiMjIEltcG9ydCAmIE1hbmFnZSBEYXRhCgotICMjIyMgU2VhbWxlc3MgYnVsayB0ZXh0IGZpbGUgaW1wb3J0Ci0gIyMjIyBEYXRhIGh5Z2llbmUgc3VwcG9ydAotICMjIyMgQW5ub3RhdGUgc291cmNlcyB3aXRoIGNvbW1lbnRzCgpgYGB7c3FsLCBldmFsPUZBTFNFfQpDUkVBVEUgVEFCTEUgZ2xvYmFsX3RlbXBlcmF0dXJlICgKICAgaWQgU0VSSUFMIE5PVCBOVUxMIFBSSU1BUlkgS0VZLAogICB5ZWFyIElOVCwKICAgcGVyaW9kIFZBUkNIQVIoNTApLAogICBnbG9iYWxfbWVhbiBOVU1FUklDKDUsMikKKTsKClxjb3B5IGdsb2JhbF90ZW1wZXJhdHVyZSAoeWVhciwgcGVyaW9kLCBnbG9iYWxfbWVhbikgRlJPTSAnL2hvbWUvcGFyZmFpdGcvRGF0YWJhc2VzL1NRTF9TZXJ2ZXIvRU5WSVJPTk1FTlQvZ2xvYmFsX3RlbXBlcmF0dXJlLmNzdicgREVMSU1JVEVSICcsJyBDU1YgSEVBREVSOwoKQ09NTUVOVCBPTiBUQUJMRSBnbG9iYWxfdGVtcGVyYXR1cmUgSVMgJ1NvdXJjZTogTkFTQSAtIENvbWJpbmVkIExhbmQtU3VyZmFjZSBBaXIgYW5kIFNlYS1TdXJmYWNlIFdhdGVyIFRlbXBlcmF0dXJlIEFub21hbGllcyAoTGFuZC1PY2VhbiBUZW1wZXJhdHVyZSBJbmRleCwgTE9USSkgKGh0dHBzOi8vZGF0YS5naXNzLm5hc2EuZ292L2dpc3RlbXAvKSc7CmBgYAoKYGBge3NxbCBjb25uZWN0aW9uPWNvbm4sIG91dHB1dC52YXIgPSAiY29tbWVudHNfZGYifQpTRUxFQ1QgcmVsbmFtZSBhcyB0YWJsZSwgb2JqX2Rlc2NyaXB0aW9uKG9pZCkgYXMgY29tbWVudApGUk9NIHBnX2NsYXNzCldIRVJFIHJlbGtpbmQgPSAncicKICBBTkQgb2JqX2Rlc2NyaXB0aW9uKG9pZCkgSVMgTk9UIE5VTEwKICBPUkRFUiBCWSByZWxuYW1lCmBgYAoKYGBge3J9CmthYmxlX3N0eWxpbmcoa2FibGUoY29tbWVudHNfZGYpLAogICAgICAgICAgICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCjxociBzdHlsZT0iYm9yZGVyOiBub25lOyBoZWlnaHQ6IDFweDsgYmFja2dyb3VuZC1jb2xvcjogI0NDQzsiLz4KCiMjIEFuYWx5emUgRGF0YQoKLSAjIyMjIERlY2FsYXJhdGl2ZSBTUUwgdG8gaW50ZXJhY3Qgd2l0aCBtYW55IGRhdGEKLSAjIyMjIExldmVyYWdlIHF1ZXJ5IGVuZ2luZSBmb3IgZGF0YSBwcm9jZXNzaW5nCi0gIyMjIyBSZWFkYWJsZSwgbWFpbnRhaW5hYmxlLCB0cmFuZmVyYWJsZQoKPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQiPjxhIGhyZWY9Imh0dHBzOi8vd3d3LmVzcmwubm9hYS5nb3YvZ21kL29ib3AvbWxvLyIgYmxhbms9InRhcmdldCI+PGltZyBzcmM9IklNQUdFUy9ub2FhLnBuZyIgaGVpZ2h0PSI1MHB4Ii8+PC9hPjwvZGl2Pgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGEgaHJlZj0iaHR0cHM6Ly93d3cuZWlhLmdvdi90b3RhbGVuZXJneS9kYXRhL21vbnRobHkvIiBibGFuayA9InRhZ2V0Ij48aW1nIHNyYz0iSU1BR0VTL2RvZV9laWEucG5nIiBoZWlnaHQ9IjUwcHgiLz48L2E+PC9kaXY+CgoKIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPkRlcGFydG1lbnQgb2YgRW5lcmd5J3MgRUlBOiBNb250aGx5IEVuZXJneSBSZXZpZXcgJjwvc3Bhbj4KIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPk5PQUEgTWF1bmEgTG9hLCBIYXdhaWkgT2JzZXJ2YXRvcnkgLSBDYXJib24gUFBNPC9zcGFuPgoKYGBge3NxbCBjb25uZWN0aW9uPWNvbm4sIG91dHB1dC52YXIgPSAibWV0cmljc19kZiJ9ClNFTEVDVCBwLmRhdGVfeWVhciwgcC5kYXRlX21vbnRoLCBDT05DQVQocC5kYXRlX3llYXIsICctJywgcC5kYXRlX21vbnRoLCAnLTAxJyk6OmRhdGUgYXMgZGF0ZV9kYXksCiAgICAgICAgICAgICAgIHAuYXZlcmFnZV9wcG0gYXMgImNhcmJvbiBwcG0iLCBjLmVuZXJneV9jb25zdW1lZCBhcyAiYnR1IGNvbnN1bWVkIiwgZS5lbmVyZ3lfY28yIGFzICJjbzIgZW1pc3Npb25zIgogIEZST00gcHBtX21vbnRoIHAKICBKT0lOIGNvbnN1bXB0aW9uIGMgCiAgICBPTiBwLmRhdGVfeWVhciA9IGMuZGF0ZV95ZWFyIEFORCBwLmRhdGVfbW9udGggPSBjLmRhdGVfbW9udGggYW5kIGMubXNuID0gJ1RYUkNCVVMnCiAgSk9JTiB1c19jbzJfZW1pc3Npb25zIGUKICAgIE9OIHAuZGF0ZV95ZWFyID0gZS5kYXRlX3llYXIgQU5EIHAuZGF0ZV9tb250aCA9IGUuZGF0ZV9tb250aCBhbmQgZS5tc24gPSAnVEVUQ0VVUycKICBPUkRFUiBCWSBwLmRhdGVfeWVhciwgcC5kYXRlX21vbnRoCmBgYAoKYGBge3NxbCBjb25uZWN0aW9uPWNvbm4sIG91dHB1dC52YXIgPSAiYWdnX2RmIn0KU0VMRUNUIHAuZGF0ZV95ZWFyLAogICAgICAgc3VtKHAuYXZlcmFnZV9wcG0pIGFzIGNhcmJvbl9wcG1fdG90YWwsIAogICAgICAgYXZnKHAuYXZlcmFnZV9wcG0pIGFzIGNhcmJvbl9wcG1fbWVhbiwgCiAgICAgICBzdW0oYy5lbmVyZ3lfY29uc3VtZWQpIGFzIGJ0dV9jb25zdW1lZF90b3RhbCwKICAgICAgIGF2ZyhjLmVuZXJneV9jb25zdW1lZCkgYXMgYnR1X2NvbnN1bWVkX21lYW4sCiAgICAgICBzdW0oZS5lbmVyZ3lfY28yKSBhcyBjbzJfZW1pc3Npb25zX3RvdGFsLAogICAgICAgYXZnKGUuZW5lcmd5X2NvMikgYXMgY28yX2VtaXNzaW9uc19tZWFuCiAgRlJPTSBwcG1fbW9udGggcAogIEpPSU4gY29uc3VtcHRpb24gYyAKICAgIE9OIHAuZGF0ZV95ZWFyID0gYy5kYXRlX3llYXIgQU5EIHAuZGF0ZV9tb250aCA9IGMuZGF0ZV9tb250aCBhbmQgYy5tc24gPSAnVFhSQ0JVUycKICBKT0lOIHVzX2NvMl9lbWlzc2lvbnMgZQogICAgT04gcC5kYXRlX3llYXIgPSBlLmRhdGVfeWVhciBBTkQgcC5kYXRlX21vbnRoID0gZS5kYXRlX21vbnRoIGFuZCBlLm1zbiA9ICdURVRDRVVTJwpHUk9VUCBCWSBwLmRhdGVfeWVhcgpPUkRFUiBCWSBwLmRhdGVfeWVhcgpgYGAKCmBgYHtyfQprYWJsZV9zdHlsaW5nKGthYmxlKHRhaWwoYWdnX2RmLCAxMCkpLAogICAgICAgICAgICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCmBgYHtyIGZpZzFhLCBmaWcuaGVpZ2h0ID0gNywgZmlnLndpZHRoID0gMTMsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQptZXRyaWNfdHMgPC0geHRzKHg9bWV0cmljc19kZltjKCJjYXJib24gcHBtIiwgImJ0dSBjb25zdW1lZCIsICJjbzIgZW1pc3Npb25zIildLCAKICAgICAgICAgICAgICAgICBvcmRlci5ieT1tZXRyaWNzX2RmJGRhdGVfZGF5KQoKcGxvdChtZXRyaWNfdHMsIG1haW4gPSAiRW5lcmd5IGFuZCBDYXJib24gUFBNIE1ldHJpY3MiLAogICAgIGxlZ2VuZC5sb2M9ImJvdHRvbXJpZ2h0IiwgCiAgICAgY29sID0gc2VhYm9ybl9wYWxldHRlWzE6M10sCiAgICAgeWF4aXMucmlnaHQ9RkFMU0UsCiAgICAgYXhlcz1GQUxTRSwKICAgICBsd2Q9MSwKICAgICBjZXgubWFpbj0zLAogICAgIG1ham9yLnRpY2tzPSJ5ZWFycyIsCiAgICAgbWFqb3IuZm9ybWF0PSIlWSIsCiAgICAgbWlub3IuZm9ybWF0PSIlWSIsCiAgICAgZ3JpZC50aWNrcy5sdHk9MSkKYGBgCgojIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+U2Vhc29uYWwgRGVjb21wb3NpdGlvbjwvc3Bhbj4KCmBgYHtyICBmaWcxYiwgZmlnLmhlaWdodCA9IDcsIGZpZy53aWR0aCA9IDEzLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KY2FyYm9ucHBtX3RzIDwtIHRzKG1ldHJpY3NfZGYkYGNhcmJvbiBwcG1gLCBzdGFydD1jKDE5NzMsIDEpLCBmcmVxdWVuY3k9MTIpCmNhcmJvbnBwbV9zdGwgPC0gc3RsKGNhcmJvbnBwbV90cywgcy53aW5kb3c9InBlcmlvZGljIikKCnBsb3QoY2FyYm9ucHBtX3N0bCwgY29sID0gc2VhYm9ybl9wYWxldHRlWzFdLCAKICAgICBtYWluPSJTZWFzb25hbCBEZWNvbXBvc2l0aW9uIG9mIEdsb2JhbCBDYXJib24gUFBNIikKYGBgCgpgYGB7ciAgZmlnMWMsIGZpZy5oZWlnaHQgPSA3LCBmaWcud2lkdGggPSAxMywgZmlnLmFsaWduID0gImNlbnRlciJ9CmNvbnN1bWVkX3RzIDwtIHRzKG1ldHJpY3NfZGYkYGJ0dSBjb25zdW1lZGAsIHN0YXJ0PWMoMTk3MywgMSksIGZyZXF1ZW5jeT0xMikKY29uc3VtZWRfc3RsIDwtIHN0bChjb25zdW1lZF90cywgcy53aW5kb3c9InBlcmlvZGljIikKCnBsb3QoY29uc3VtZWRfc3RsLCBjb2wgPSBzZWFib3JuX3BhbGV0dGVbMl0sIAogICAgIG1haW49IlNlYXNvbmFsIERlY29tcG9zaXRpb24gb2YgVS5TLiBFbmVyZ3kgQ29uc3VtcHRpb24iKQpgYGAKCmBgYHtyIGZpZzFkLCBmaWcuaGVpZ2h0ID0gNywgZmlnLndpZHRoID0gMTMsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQplbWlzc2lvbnNfdHMgPC0gdHMobWV0cmljc19kZiRgY28yIGVtaXNzaW9uc2AsIHN0YXJ0PWMoMTk3MywgMSksIGZyZXF1ZW5jeT0xMikKZW1pc3Npb25zX3N0bCA8LSBzdGwoZW1pc3Npb25zX3RzLCBzLndpbmRvdz0icGVyaW9kaWMiKQoKcGxvdChlbWlzc2lvbnNfc3RsLCBjb2wgPSBzZWFib3JuX3BhbGV0dGVbM10sCiAgICAgbWFpbiA9ICJTZWFzb25hbCBEZWNvbXBvc2l0aW9uIG9mIFUuUy4gQ08yIEVtaXNzaW9ucyIpCmBgYAoKYGBge3J9CnNxbCA8LSAiV0lUSCBjMSBBUwogICAgICAgICAgIChTRUxFQ1QgQ09OQ0FUKChkYXRlX3llYXIvMTApOjppbnQgKiAxMCwgJ3MnKSBhcyBkZWNhZGUsIAogICAgICAgICAgICAgICAgICAgUkVQTEFDRShkZXNjcmlwdGlvbiwgJ1ByaW1hcnkgRW5lcmd5IENvbnN1bWVkIGJ5IHRoZSAnLCAnJykgQVMgXCJTZWN0b3JcIiwKICAgICAgICAgICAgICAgICAgIFNVTShlbmVyZ3lfY29uc3VtZWQpIEFTIFwiUHJpbWFyeSBFbmVyZ3kgQ29uc3VtZWRcIgogICAgICAgICAgICBGUk9NIGNvbnN1bXB0aW9uCiAgICAgICAgICAgIFdIRVJFIG1zbiBJTiAoJ1RYSUNCVVMnLCAnVFhSQ0JVUycsICdUWEFDQlVTJywgJ1RYQ0NCVVMnLCAnVFhFSUJVUycpCiAgICAgICAgICAgIEdST1VQIEJZIENPTkNBVCgoZGF0ZV95ZWFyLzEwKTo6aW50ICogMTAsICdzJyksIGRlc2NyaXB0aW9uCiAgICAgICAgICAgKSwgYzIgQVMKICAgICAgICAgICAoU0VMRUNUIENPTkNBVCgoZGF0ZV95ZWFyLzEwKTo6aW50ICogMTAsICdzJykgYXMgZGVjYWRlLCAKICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoZGVzY3JpcHRpb24sICdUb3RhbCBFbmVyZ3kgQ29uc3VtZWQgYnkgdGhlICcsICcnKSBBUyBcIlNlY3RvclwiLAogICAgICAgICAgICAgICAgICAgU1VNKGVuZXJneV9jb25zdW1lZCkgQVMgXCJUb3RhbCBFbmVyZ3kgQ29uc3VtZWRcIgogICAgICAgICAgICBGUk9NIGNvbnN1bXB0aW9uCiAgICAgICAgICAgIFdIRVJFIG1zbiBJTiAoJ1RFQ0NCVVMnLCAnVEVBQ0JVUycsICdURUlDQlVTJywgJ1RFUkNCVVMnKQogICAgICAgICAgICBHUk9VUCBCWSBDT05DQVQoKGRhdGVfeWVhci8xMCk6OmludCAqIDEwLCAncycpLCBkZXNjcmlwdGlvbgogICAgICAgICAgICkKCiAgICAgICAgU0VMRUNUIGMxLmRlY2FkZSwgYzEuXCJTZWN0b3JcIiwgYzEuXCJQcmltYXJ5IEVuZXJneSBDb25zdW1lZFwiLCBjMi5cIlRvdGFsIEVuZXJneSBDb25zdW1lZFwiCiAgICAgICAgRlJPTSBjMQogICAgICAgIExFRlQgSk9JTiBjMgogICAgICAgICAgICBPTiBjMS5cIlNlY3RvclwiID0gYzIuXCJTZWN0b3JcIiBBTkQgYzEuZGVjYWRlID0gYzIuZGVjYWRlCiAgICAgICAgT1JERVIgQlkgYzEuZGVjYWRlLCBjMS5cIlNlY3RvclwiCiAgICAgICAiCgpjb25zdW1lZF9kZiA8LSBkYkdldFF1ZXJ5KGNvbm4sIHNxbCkKCmthYmxlX3N0eWxpbmcoa2FibGUodGFpbChjb25zdW1lZF9kZikpLAogICAgICAgICAgICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0Ij48aW1nIHNyYz0iSU1BR0VTL2RvZV9laWEucG5nIiB3aWR0aD0iNzVweCIvPjwvZGl2PgojIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+Q29uc3VtcHRpb24gYW5kIENPMiBFbWlzc2lvbnM8L3NwYW4+CgoKYGBge3IgZmlnMWUsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSAxNSwgZmlnLmFsaWduID0gImNlbnRlciJ9CnBsb3RfbWF0IDwtIHdpdGgoc3Vic2V0KGNvbnN1bWVkX2RmLCBTZWN0b3IgIT0gJ0VsZWN0cmljIFBvd2VyIFNlY3RvcicpLAogICAgICAgICAgICAgICAgIHRhcHBseShgVG90YWwgRW5lcmd5IENvbnN1bWVkYCwgbGlzdChkZWNhZGUsIGBTZWN0b3JgKSwgc3VtKSkKCnBhcihtYXI9Yyg1LCA1LCAyLCAxKSkKYmFycGxvdChwbG90X21hdCwgbWFpbj0iVS5TLiBUb3RhbCBFbmVyZ3kgQ29uc3VtcHRpb24gYnkgU2VjdG9yIiwgY2V4Lm1haW49MS41LAogICAgICAgIGNvbD1zZWFib3JuX3BhbGV0dGVbMTo4XSwgeWxpbT1jKDAsIDhFNSksIHhheHQ9Im4iLCB5YXh0PSJuIiwgYmVzaWRlPVRSVUUpCgpheGlzKHNpZGU9MSwgYXQ9Yyg1LCAxNCwgMjMsIDMyKSwgbGFiZWxzPWNvbG5hbWVzKHBsb3RfbWF0KSwgcGFkaj0wLjksIHBvcz1jKDAsMCkpCmF4aXMoc2lkZT0yLCBhdD1heFRpY2tzKDIpLCBsYWJlbHM9Zm9ybWF0KGF4VGlja3MoMiksIGJpZy5tYXJrPScsJywgc2NpZW50aWZpYz1GQUxTRSksIGxhcz0xKQpsZWdlbmQoInRvcCIsIHJvdy5uYW1lcyhwbG90X21hdCksIGZpbGw9c2VhYm9ybl9wYWxldHRlWzE6OF0sIG5jb2w9OCkKYGBgCgpgYGB7ciBmaWcxZiwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDE1LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KcGxvdF9tYXQgPC0gd2l0aChjb25zdW1lZF9kZiwgdGFwcGx5KGBQcmltYXJ5IEVuZXJneSBDb25zdW1lZGAsIGxpc3QoZGVjYWRlLCBgU2VjdG9yYCksIHN1bSkpCgpwYXIobWFyPWMoNSwgNSwgMiwgMSkpCmJhcnBsb3QocGxvdF9tYXQsIG1haW49IlUuUy4gUHJpbWFyeSBFbmVyZ3kgQ29uc3VtcHRpb24gYnkgU2VjdG9yIiwgY2V4Lm1haW49MS41LAogICAgICAgIGNvbD1zZWFib3JuX3BhbGV0dGVbMTo4XSwgeWxpbT1jKDAsIDlFNSksIHhheHQ9Im4iLCB5YXh0PSJuIiwgYmVzaWRlPVRSVUUpCgpheGlzKHNpZGU9MSwgYXQ9Yyg1LCAxNCwgMjMsIDMyLCA0MSksIGxhYmVscz1jb2xuYW1lcyhwbG90X21hdCksIHBhZGo9MC45LCBwb3M9YygwLDApKQpheGlzKHNpZGU9MiwgYXQ9YXhUaWNrcygyKSwgbGFiZWxzPWZvcm1hdChheFRpY2tzKDIpLCBiaWcubWFyaz0nLCcsIHNjaWVudGlmaWM9RkFMU0UpLCBsYXM9MSkKbGVnZW5kKCJ0b3AiLCByb3cubmFtZXMocGxvdF9tYXQpLCBmaWxsPXNlYWJvcm5fcGFsZXR0ZVsxOjhdLCBuY29sPTgpCmBgYAoKYGBge3J9CnNxbCA8LSAiU0VMRUNUIENPTkNBVCgoZGF0ZV95ZWFyLzEwKTo6aW50ICogMTAsICdzJykgYXMgZGVjYWRlLCAKICAgICAgICAgICAgICAgUkVQTEFDRShSRVBMQUNFKGRlc2NyaXB0aW9uLCAnVG90YWwgRW5lcmd5ICcsICcnKSwgJyBDTzIgRW1pc3Npb25zJywgJycpIEFTIFwiU2VjdG9yXCIsCiAgICAgICAgICAgICAgIFNVTShlbmVyZ3lfY28yKSBBUyBcIlRvdGFsIENPMiBFbWlzc2lvbnNcIgogICAgICAgIEZST00gdXNfY28yX2VtaXNzaW9ucwogICAgICAgIFdIRVJFIG1zbiBJTiAoJ1RFUkNFVVMnLCAnVEVDQ0VVUycsICdURUFDRVVTJywgJ1RYRUlFVVMnKQogICAgICAgIEdST1VQIEJZIGRhdGVfeWVhciwgZGVzY3JpcHRpb24KICAgICAgICBPUkRFUiBCWSBkYXRlX3llYXIsIGRlc2NyaXB0aW9uCiAgICAgICAiCgplbWlzc2lvbnNfZGYgPC0gZGJHZXRRdWVyeShjb25uLCBzcWwpCgprYWJsZV9zdHlsaW5nKGthYmxlKHRhaWwoZW1pc3Npb25zX2RmKSksCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKYGBge3IgZmlnMWcsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSAxNSwgZmlnLmFsaWduID0gImNlbnRlciJ9CnBsb3RfbWF0IDwtIHdpdGgoZW1pc3Npb25zX2RmLCB0YXBwbHkoYFRvdGFsIENPMiBFbWlzc2lvbnNgLCBsaXN0KGRlY2FkZSwgYFNlY3RvcmApLCBzdW0pKQoKcGFyKG1hcj1jKDUsIDgsIDIsIDEpKQpiYXJwbG90KHBsb3RfbWF0LCBtYWluPSJVLlMuIENPMiBFbWlzc2lvbnMgYnkgU2VjdG9yIiwgY2V4Lm1haW49MS41LAogICAgICAgIGNvbD1zZWFib3JuX3BhbGV0dGVbMTo1XSwgeWxpbT1jKDAsIDZFNCksIHhheHQ9Im4iLCB5YXh0PSJuIiwgYmVzaWRlPVRSVUUpCgpheGlzKHNpZGU9MSwgYXQ9YygzLCA5LCAxNSwgMjEpKzAuNSwgbGFiZWxzPWNvbG5hbWVzKHBsb3RfbWF0KSwgcGFkaj0wLjksIHBvcz1jKDAsMCkpCmF4aXMoc2lkZT0yLCBhdD1heFRpY2tzKDIpLCBsYWJlbHM9Zm9ybWF0KGF4VGlja3MoMiksIGJpZy5tYXJrPScsJywgc2NpZW50aWZpYz1GQUxTRSksIGxhcz0xKQpsZWdlbmQoInRvcCIsIHJvdy5uYW1lcyhwbG90X21hdCksIGZpbGw9c2VhYm9ybl9wYWxldHRlWzE6NV0sIG5jb2w9NSkKYGBgCgoKYGBge3J9CnNxbCA8LSAiV0lUSCBzdWIgQVMKICAgICAgICAgIChTRUxFQ1QgQ09OQ0FUKChkYXRlX3llYXIvMTApOjppbnQgKiAxMCwgJ3MnKSBhcyBkZWNhZGUsIAogICAgICAgICAgICAgICAgICBSRVBMQUNFKCQxLCAnJScsICcnKSBhcyBzZWN0b3IsCiAgICAgICAgICAgICAgICAgIFJFUExBQ0UoCiAgICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoCiAgICAgICAgICAgICAgICAgICAgICAgUkVQTEFDRSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoZGVzY3JpcHRpb24sICdDb21tZXJjaWFsIFNlY3RvciBDTzIgRW1pc3Npb25zJywgJycpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdSZXNpZGVudGlhbCBTZWN0b3IgQ08yIEVtaXNzaW9ucycsICcnCiAgICAgICAgICAgICAgICAgICAgICAgKSwgJ1RyYW5zcG9ydGF0aW9uIFNlY3RvciBDTzIgRW1pc3Npb25zJywgJycpLAogICAgICAgICAgICAgICAgICAnICcsICdcbicpICBBUyBcIlR5cGVcIiwKICAgICAgICAgICAgICAgICBlbmVyZ3lfY28yCiAgICAgICAgICAgRlJPTSB1c19jbzJfZW1pc3Npb25zCiAgICAgICAgICAgV0hFUkUgZGVzY3JpcHRpb24gTElLRSAkMikKCiAgICAgICBTRUxFQ1QgZGVjYWRlLCBzZWN0b3IsIFwiVHlwZVwiLCAKICAgICAgICAgICAgICBTVU0oZW5lcmd5X2NvMikgQVMgXCJUb3RhbCBDTzIgRW1pc3Npb25zXCIKICAgICAgIEZST00gc3ViCiAgICAgICBHUk9VUCBCWSBkZWNhZGUsIHNlY3RvciwgXCJUeXBlXCIKICAgICAgIE9SREVSIEJZIGRlY2FkZSwgc2VjdG9yLCBcIlR5cGVcIgogICAgICAgIgoKcGFyYW1zIDwtIHBhc3RlMChjKCIlVHJhbnNwb3J0YXRpb24iLCAiJVJlc2lkZW50aWFsIiwgIiVDb21tZXJjaWFsIiksICIgU2VjdG9yJSIpCmVtaXNzaW9uc190eXBlX2RmIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseShwYXJhbXMsIGZ1bmN0aW9uKHApIGRiR2V0UXVlcnkoY29ubiwgc3FsLCBwYXJhbT1saXN0KHAsIHApKSkpCmBgYAoKYGBge3IgZmlnMWgsIGZpZy5oZWlnaHQgPSAxMiwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQoKcGFyKG1mcm93PWMoMywyKSwgbWFyPWMoNSwgNSwgMiwgMSksIG1haSA9IGMoMC43LCAwLjIsIDAuNywgMC4yKSkKb3V0cHV0IDwtIGJ5KGVtaXNzaW9uc190eXBlX2RmLCBlbWlzc2lvbnNfdHlwZV9kZiRzZWN0b3IsIGZ1bmN0aW9uKHN1YikgewogIHBsb3RfbWF0IDwtIHdpdGgoc3ViLCB0YXBwbHkoYFRvdGFsIENPMiBFbWlzc2lvbnNgLCBsaXN0KGRlY2FkZSwgYFR5cGVgKSwgc3VtKSkKCiAgYmFycGxvdChwbG90X21hdCwgbWFpbj1wYXN0ZSgiVS5TLiBDTzIgRW1pc3Npb25zIGJ5Iiwgc3ViJHNlY3RvcltbMV1dKSwgY2V4Lm1haW49MS41LAogICAgICAgICAgY29sPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCB5bGltPWMoMCwgY2VpbGluZyhtYXgocGxvdF9tYXQsIG5hLnJtPVRSVUUpLzFFNCkgKiAxRTQpLCAKICAgICAgICAgIHhheHQ9Im4iLCB5YXh0PSJuIiwgYmVzaWRlPVRSVUUpCiAgCiAgYXhpcyhzaWRlPTEsIGF0PWMoMyw5LDE1LDIxLDI3LDMzLDM5LDQ1LDUxLDU3LDYzKVtzZXFfYWxvbmcoY29sbmFtZXMocGxvdF9tYXQpKV0gKyAwLjUsCiAgICAgICBsYWJlbHM9Y29sbmFtZXMocGxvdF9tYXQpLCBwYWRqPTAuOSwgcG9zPWMoMCwwKSkKICBheGlzKHNpZGU9MiwgYXQ9YXhUaWNrcygyKSwgbGFiZWxzPWZvcm1hdChheFRpY2tzKDIpLCBiaWcubWFyaz0nLCcsIHNjaWVudGlmaWM9RkFMU0UpLCBsYXM9MSkKICBsZWdlbmQoInRvcCIsIHJvdy5uYW1lcyhwbG90X21hdCksIGZpbGw9c2VhYm9ybl9wYWxldHRlWzE6NV0sIG5jb2w9NSkKfSkKYGBgCgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGltZyBzcmM9IklNQUdFUy9kb2VfZWlhLnBuZyIgd2lkdGg9Ijc1cHgiLz48L2Rpdj4KIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPlJlbmV3YWJsZSBFbmVyZ3k6IFByb2R1Y3Rpb24gYW5kIENvbnN1bXB0aW9uPC9zcGFuPgoKYGBge3J9CnNxbCA8LSAiU0VMRUNUIGVuZXJneV90eXBlLAogICAgICAgICAgICAgICBkYXRlLAogICAgICAgICAgICAgICBTVU0ocHJvZHVjdGlvbikgQVMgcHJvZHVjdGlvbiwKICAgICAgICAgICAgICAgU1VNKGNvbnN1bXB0aW9uKSBBUyBjb25zdW1wdGlvbgogICAgICAgIEZST00gdXNfcmVuZXdhYmxlX2VuZXJneQogICAgICAgIEdST1VQIEJZIGVuZXJneV90eXBlLAogICAgICAgICAgICAgICAgIGRhdGUKICAgICAgICBPUkRFUiBCWSBlbmVyZ3lfdHlwZSwKICAgICAgICAgICAgICAgICBkYXRlCiAgICAgICAiCgpyZW5ld2FibGVfZGYgPC0gZGJHZXRRdWVyeShjb25uLCBzcWwpCgprYWJsZV9zdHlsaW5nKGthYmxlKHRhaWwocmVuZXdhYmxlX2RmKSksCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKYGBge3IgcmVuZXdhYmxlX2ZpZywgZmlnLmhlaWdodCA9IDE1LCBmaWcud2lkdGggPSAxNSwgZmlnLmFsaWduID0gImNlbnRlciJ9CgpwYXIobWZyb3c9YygzLDMpLCBtYXI9Yyg1LCA1LCAyLCAxKSkKb3V0cHV0IDwtIGJ5KHJlbmV3YWJsZV9kZiwgcmVuZXdhYmxlX2RmJGVuZXJneV90eXBlLCBmdW5jdGlvbihzdWIpIHsKICAKICBtZXRyaWNfdHMgPC0geHRzKHg9c3ViW2MoInByb2R1Y3Rpb24iLCAiY29uc3VtcHRpb24iKV0sIAogICAgICAgICAgICAgICAgICAgb3JkZXIuYnk9c3ViJGRhdGUpCiAgCiAgcHJpbnQocGxvdChtZXRyaWNfdHMsIG1haW4gPSBzdWIkZW5lcmd5X3R5cGVbMV0sCiAgICAgICAgICAgICBsZWdlbmQubG9jPSJib3R0b21yaWdodCIsIAogICAgICAgICAgICAgY29sID0gc2VhYm9ybl9wYWxldHRlWzE6M10sCiAgICAgICAgICAgICB5YXhpcy5yaWdodD1GQUxTRSwKICAgICAgICAgICAgIGF4ZXM9RkFMU0UsCiAgICAgICAgICAgICBsd2Q9MSwKICAgICAgICAgICAgIGNleC5tYWluPTMsCiAgICAgICAgICAgICBtYWpvci50aWNrcz0ieWVhcnMiLAogICAgICAgICAgICAgbWFqb3IuZm9ybWF0PSIlWSIsCiAgICAgICAgICAgICBtaW5vci5mb3JtYXQ9IiVZIiwKICAgICAgICAgICAgIGdyaWQudGlja3MubHR5PTEpKQogIAp9KQpgYGAKCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0Ij48YSBocmVmPSJodHRwczovL3dhdGVyZGF0YS51c2dzLmdvdi9ud2lzL2d3Ij48aW1nIHNyYz0iSU1BR0VTL3VzZ3MucG5nIiB3aWR0aD0iNzVweCIvPjwvYT48L2Rpdj4KCiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5VLlMuIEdlb2xvZ2ljYWwgU3VydmV5IC0gR3JvdW5kd2F0ZXIgV2VsbCBEZXB0aCBEYXRhPC9zcGFuPgpgYGB7cn0Kc3FsIDwtICJTRUxFQ1QgZy55ZWFyLCBnLm1vbnRoLAogICAgICAgICAgICAgICBhdmcoZy5tZWFuX3ZhbHVlKSBhcyBtZWFuX3ZhbHVlCiAgICAgICAgRlJPTSBncm91bmR3YXRlciBnCiAgICAgICAgV0hFUkUgZy55ZWFyIEJFVFdFRU4gMTk5MCBBTkQgMjAxOQogICAgICAgIEdST1VQIEJZIGcueWVhciwgZy5tb250aCIKCmdyb3VuZHdhdGVyX2RmIDwtIGRiR2V0UXVlcnkoY29ubiwgc3FsKQoKa2FibGVfc3R5bGluZyhrYWJsZSh0YWlsKGdyb3VuZHdhdGVyX2RmKSksCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKYGBge3IgZmlnMywgZmlnLmhlaWdodCA9IDcsIGZpZy53aWR0aCA9IDE2LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KYm94cGxvdChtZWFuX3ZhbHVlIH4geWVhciwgZ3JvdW5kd2F0ZXJfZGYsIGNvbD1zZWFib3JuX3BhbGV0dGVbMToxMF0sIAogICAgICAgIG1haW49Ikdyb3VuZHdhdGVyIFdlbGwgRGVwdGggTWVhbiBWYWx1ZXMiLCBjZXgubWFpbj0xLjUpCmBgYAoKCmBgYHtyfQpzcWwgPC0gIldJVEggc2l0ZXMgQVMgKAogICAgICAgICAgICBTRUxFQ1QgQ09OQ0FUKGcueWVhciwgJy0nLCBnLm1vbnRoLCAnLTEnKTo6ZGF0ZSBBUyBcImRhdGVcIiwKICAgICAgICAgICAgICAgICAgIGcuc2l0ZV9uYW1lLAogICAgICAgICAgICAgICAgICAgQVZHKGcubWVhbl92YWx1ZSkgYXMgbWVhbl92YWx1ZQogICAgICAgICAgICBGUk9NIGdyb3VuZHdhdGVyIGcKICAgICAgICAgICAgV0hFUkUgZy55ZWFyIEJFVFdFRU4gMTk5MCBBTkQgMjAxOQogICAgICAgICAgICBHUk9VUCBCWSBDT05DQVQoZy55ZWFyLCAnLScsIGcubW9udGgsICctMScpOjpkYXRlLAogICAgICAgICAgICAgICAgICAgICBnLnNpdGVfbmFtZQogICAgICAgICksIGJvdHRvbSBBUyAoCiAgICAgICAgICAgIFNFTEVDVCBzaXRlX25hbWUsIEFWRyhtZWFuX3ZhbHVlKSBBUyBtZWFuX3ZhbHVlLCAnYm90dG9tX3NpdGVzJyBBUyBjYXRlZ29yeQogICAgICAgICAgICBGUk9NIHNpdGVzIAogICAgICAgICAgICBHUk9VUCBCWSBzaXRlX25hbWUKICAgICAgICAgICAgT1JERVIgQlkgQVZHKG1lYW5fdmFsdWUpIEFTQyBMSU1JVCA1CiAgICAgICAgKSwgdG9wIEFTICgKICAgICAgICAgICAgU0VMRUNUIHNpdGVfbmFtZSwgQVZHKG1lYW5fdmFsdWUpIEFTIG1lYW5fdmFsdWUsICd0b3Bfc2l0ZXMnIEFTIGNhdGVnb3J5CiAgICAgICAgICAgIEZST00gc2l0ZXMgCiAgICAgICAgICAgIEdST1VQIEJZIHNpdGVfbmFtZQogICAgICAgICAgICBPUkRFUiBCWSBBVkcobWVhbl92YWx1ZSkgREVTQyBMSU1JVCA1CiAgICAgICAgKQoKICAgICAgICBTRUxFQ1Qgc2l0ZXMuXCJkYXRlXCIsIHNpdGVzLm1lYW5fdmFsdWUsIHNpdGVzLnNpdGVfbmFtZSwgdG9wLmNhdGVnb3J5CiAgICAgICAgRlJPTSBzaXRlcwogICAgICAgIElOTkVSIEpPSU4gdG9wCiAgICAgICAgICAgIE9OIHNpdGVzLnNpdGVfbmFtZSA9IHRvcC5zaXRlX25hbWUKCiAgICAgICAgVU5JT04KCiAgICAgICAgU0VMRUNUIHNpdGVzLlwiZGF0ZVwiLCBzaXRlcy5tZWFuX3ZhbHVlLCBzaXRlcy5zaXRlX25hbWUsIGJvdHRvbS5jYXRlZ29yeQogICAgICAgIEZST00gc2l0ZXMKICAgICAgICBJTk5FUiBKT0lOIGJvdHRvbQogICAgICAgICAgICBPTiBzaXRlcy5zaXRlX25hbWUgPSBib3R0b20uc2l0ZV9uYW1lCgogICAgICAgIE9SREVSIEJZIGNhdGVnb3J5LCBzaXRlX25hbWUsIFwiZGF0ZVwiIgoKZ3JvdW5kd2F0ZXJfZGYgPC0gZGJHZXRRdWVyeShjb25uLCBzcWwpCgprYWJsZV9zdHlsaW5nKGthYmxlKGhlYWQoZ3JvdW5kd2F0ZXJfZGYpKSwKICAgICAgICAgICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKYGBgCgpgYGB7ciBncm91bmR3YXRlcl9maWcsIGZpZy5oZWlnaHQgPSAyNSwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQoKZ3JvdW5kd2F0ZXJfZGYkeWVhciA8LSBmb3JtYXQoZ3JvdW5kd2F0ZXJfZGYkZGF0ZSwgIiVZIikKCnBhcihtZnJvdz1jKDUsMiksIG1hcj1jKDUsIDUsIDIsIDEpKQpvdXRwdXQgPC0gYnkoZ3JvdW5kd2F0ZXJfZGYsIGdyb3VuZHdhdGVyX2RmJHNpdGVfbmFtZSwgZnVuY3Rpb24oc3ViKSB7CiAgCiAgbWV0cmljX3RzIDwtIHh0cyh4PXN1YltjKCJtZWFuX3ZhbHVlIildLCAKICAgICAgICAgICAgICAgICAgIG9yZGVyLmJ5PXN1YiRkYXRlKQogIAogIHByaW50KHBsb3QobWV0cmljX3RzLCBtYWluID0gcGFzdGUoc3ViJHNpdGVfbmFtZVsxXSwgJzonLCBzdWIkY2F0ZWdvcnlbMV0pLAogICAgICAgICAgICAgbGVnZW5kLmxvYz0iYm90dG9tcmlnaHQiLCAKICAgICAgICAgICAgIGNvbCA9IHNlYWJvcm5fcGFsZXR0ZVsxOjNdLAogICAgICAgICAgICAgeWF4aXMucmlnaHQ9RkFMU0UsCiAgICAgICAgICAgICBheGVzPUZBTFNFLAogICAgICAgICAgICAgbHdkPTEsCiAgICAgICAgICAgICBjZXgubWFpbj0zLAogICAgICAgICAgICAgbWFqb3IudGlja3M9InllYXJzIiwKICAgICAgICAgICAgIG1ham9yLmZvcm1hdD0iJVkiLAogICAgICAgICAgICAgbWlub3IuZm9ybWF0PSIlWSIsCiAgICAgICAgICAgICBncmlkLnRpY2tzLmx0eT0xKSkKICAKfSkKYGBgCgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGEgaHJlZj0iaHR0cHM6Ly93d3cuZndzLmdvdi9lbmRhbmdlcmVkLyIgdGFyZ2V0PSJibGFuayI+PGltZyBzcmM9IklNQUdFUy91c19md3MucG5nIiB3aWR0aD0iNzVweCIvPjwvYT48L2Rpdj4KIyMjIFUuUy4gRldTIEVuZGFuZ2VyZWQgU3BlY2llcyBMaXN0CgpgYGB7cn0Kc3FsIDwtICJTRUxFQ1QgQ09OQ0FUKChmLmRhdGVfeWVhci8xMCk6OmludCAqIDEwLCAncycpIEFTIGRlY2FkZSwgCiAgICAgICAgICAgICAgIGYudGF4b25vbWljX2dyb3VwLAogICAgICAgICAgICAgICBmLmN1cnJlbnRfc3RhdHVzLAogICAgICAgICAgICAgICBjb3VudCgqKSBBUyBzcGVjaWVzX2NvdW50CiAgICAgICAgRlJPTSBmd3Nfc3BlY2llc195ZWFyIGYKICAgICAgICBXSEVSRSBmLmN1cnJlbnRfc3RhdHVzIElOICgnRW5kYW5nZXJlZCcsICdSZWNvdmVyeScsICdSZXNvbHZlZCBUYXhvbicsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdUaHJlYXRlbmVkJywgJ0V4dGluY3Rpb24nKQogICAgICAgIEdST1VQIEJZIENPTkNBVCgoZi5kYXRlX3llYXIvMTApOjppbnQgKiAxMCwgJ3MnKSwKICAgICAgICAgICAgICAgICBmLnRheG9ub21pY19ncm91cCwKICAgICAgICAgICAgICAgICBmLmN1cnJlbnRfc3RhdHVzCiAgICAgICAgT1JERVIgQlkgQ09OQ0FUKChmLmRhdGVfeWVhci8xMCk6OmludCAqIDEwLCAncycpLAogICAgICAgICAgICAgICAgIGYudGF4b25vbWljX2dyb3VwLAogICAgICAgICAgICAgICAgIGYuY3VycmVudF9zdGF0dXMiCgpmd3NfZGYgPC0gZGJHZXRRdWVyeShjb25uLCBzcWwpCgprYWJsZV9zdHlsaW5nKGthYmxlKHRhaWwoZndzX2RmKSksCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKYGBge3IgZndzX2ZpZywgZmlnLmhlaWdodCA9IDE1LCBmaWcud2lkdGggPSAxNSwgZmlnLmFsaWduID0gImNlbnRlciJ9CgpwYXIobWZyb3c9Yyg1LDMpLCBtYXI9Yyg1LCA1LCAyLCAxKSkKb3V0cHV0IDwtIGJ5KGZ3c19kZiwgZndzX2RmJHRheG9ub21pY19ncm91cCwgZnVuY3Rpb24oc3ViKSB7CiAgcGxvdF9tYXQgPC0gd2l0aChzdWIsIHRhcHBseShzcGVjaWVzX2NvdW50LCBsaXN0KGRlY2FkZSwgY3VycmVudF9zdGF0dXMpLCBzdW0pKQogIAogIGJhcnBsb3QocGxvdF9tYXQsIG1haW49c3ViJHRheG9ub21pY19ncm91cFtbMV1dLCBjZXgubWFpbj0xLjUsCiAgICAgICAgICB5bGltID0gYygwLCBtYXgocGxvdF9tYXQsIG5hLnJtPVRSVUUpKzUpLAogICAgICAgICAgY29sPXNlYWJvcm5fcGFsZXR0ZVtzZXFfYWxvbmcocm93Lm5hbWVzKHBsb3RfbWF0KSldLCBiZXNpZGU9VFJVRSkKICBsZWdlbmQoInRvcCIsIHJvdy5uYW1lcyhwbG90X21hdCksIGZpbGw9c2VhYm9ybl9wYWxldHRlW3NlcV9hbG9uZyhyb3cubmFtZXMocGxvdF9tYXQpKV0sIAogICAgICAgICBuY29sPWxlbmd0aChyb3cubmFtZXMocGxvdF9tYXQpKSkKICBib3goKQp9KQpgYGAKCgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGEgaHJlZj0iaHR0cHM6Ly93d3cubmFzcy51c2RhLmdvdi9BZ0NlbnN1cy8iIHRhcmdldD0iYmxhbmsiPjxpbWcgc3JjPSJJTUFHRVMvdXNkYS5wbmciIHdpZHRoPSIxMDBweCIvPjwvYT48L2Rpdj4KIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPlUuUy4gRGVwYXJ0bWVudCBvZiBBZ3JpY3VsdHVyZTogQWdyaWN1bHR1cmUgQ2Vuc3VzPC9zcGFuPgoKYGBge3IgZmlnNCwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDE3LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0Kc3FsIDwtICJTRUxFQ1QgeWVhciwKICAgICAgICAgICAgICAgQ0FTRQogICAgICAgICAgICAgICAgICAgIFdIRU4gZG9tYWluX2NhdGVnb3J5ID0gJycgVEhFTiAnVE9UQUxcbk9QRVJBVElPTlMnCiAgICAgICAgICAgICAgICAgICAgRUxTRSBSRVBMQUNFKFJFUExBQ0UoUkVQTEFDRShkb21haW5fY2F0ZWdvcnksICcoJywgJ1xuKCcpLCAnVE8nLCAnVE9cbicpLCAnT1InLCAnT1JcbicpCiAgICAgICAgICAgICAgIEVORCBBUyBkb21haW5fY2F0ZWdvcnksIAogICAgICAgICAgICAgICB2YWx1ZQogICAgICAgIEZST00gYWdfY2Vuc3VzIAogICAgICAgIFdIRVJFIGRhdGFfaXRlbSA9ICdGQVJNIE9QRVJBVElPTlMgLSBOVU1CRVIgT0YgT1BFUkFUSU9OUyciCgphZ2NlbnN1c19kZiA8LSBkYkdldFF1ZXJ5KGNvbm4sIHNxbCkKCnBsb3RfbWF0IDwtIHdpdGgoYWdjZW5zdXNfZGYsIHRhcHBseSh2YWx1ZSwgbGlzdCh5ZWFyLCBkb21haW5fY2F0ZWdvcnkpLCBzdW0pKQoKcGFyKG1hcj1jKDUsIDUsIDIsIDEpKQpiYXJwbG90KHBsb3RfbWF0LCBtYWluPSJVLlMuIEFncmljdWx0dXJlIENlbnN1czogTnVtYmVyIG9mIEZhcm0gT3BlcmF0aW9ucyIsIGNleC5tYWluPTEuNSwKICAgICAgICBjb2w9c2VhYm9ybl9wYWxldHRlWzE6NV0sIHlsaW09YygwLCAyRTYrNUU1KSwgYmVzaWRlPVRSVUUsIHhheHQ9Im4iLCB5YXh0PSJuIikKCmF4aXMoc2lkZT0xLCBhdD1jKDMsOSwxNSwyMSwyNywzMywzOSw0NSkrMC41LCBsYWJlbHM9Y29sbmFtZXMocGxvdF9tYXQpLCB0aWNrPUZBTFNFLCBwYWRqPTAuOSwgcG9zPWMoMCwwKSkKYXhpcyhzaWRlPTIsIGF0PWF4VGlja3MoMiksIGxhYmVscz1mb3JtYXQoYXhUaWNrcygyKSwgYmlnLm1hcms9JywnLCBzY2llbnRpZmljPUZBTFNFKSwgbGFzPTEpCmxlZ2VuZCgidG9wIiwgcm93Lm5hbWVzKHBsb3RfbWF0KSwgZmlsbD1zZWFib3JuX3BhbGV0dGVbMTo1XSwgbmNvbD01KQpgYGAKCmBgYHtyfQpzcWwgPC0gIldJVEggc3ViIEFTICgKICAgICAgICAgIFNFTEVDVCB5ZWFyLAogICAgICAgICAgICAgICAgIGRhdGFfaXRlbSwKICAgICAgICAgICAgICAgICBSRVBMQUNFKAogICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoCiAgICAgICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoZG9tYWluX2NhdGVnb3J5LCAnOycsICcsJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcoJywgJ1xuKCcpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1RPJywgJ1RPXG4nKSwKICAgICAgICAgICAgICAgICAgICAgICAgJyBPUicsICcgT1JcbicpLAogICAgICAgICAgICAgICAgICAgICdMRVNTJywgJ0xFU1NcbicpIEFTIGRvbWFpbl9jYXRlZ29yeSwgCiAgICAgICAgICAgICAgICAgdmFsdWUKICAgICAgICAgIEZST00gYWdfY2Vuc3VzIAogICAgICAgICAgV0hFUkUgZGF0YV9pdGVtIExJS0UgJyVDT01NT0RJVFkgVE9UQUxTJScKICAgICAgICAgICAgQU5EIHZhbHVlIElTIE5PVCBOVUxMCiAgICAgICApIAoKICAgICAgIFNFTEVDVCB5ZWFyLCAKICAgICAgICAgICAgICBDQVNFCiAgICAgICAgICAgICAgICAgIFdIRU4gVFJJTShkb21haW5fY2F0ZWdvcnkpID0gJycgCiAgICAgICAgICAgICAgICAgIFRIRU4gQ0FTRSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdIRU4gZGF0YV9pdGVtID0gJ0NPTU1PRElUWSBUT1RBTFMgLSBTQUxFUzsgTUVBU1VSRUQgSU4gJCcgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUSEVOICdUT1RBTFxuU0FMRVMnCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBXSEVOIGRhdGFfaXRlbSA9ICdDT01NT0RJVFkgVE9UQUxTIC0gU0FMRVM7IE1FQVNVUkVEIElOICQgLyBPUEVSQVRJT04nIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVEhFTiAnVE9UQUxcblNBTEVTXG5QRVIgT1BFUkFUSU9OJwogICAgICAgICAgICAgICAgICAgICAgIEVORAogICAgICAgICAgICAgICAgICBFTFNFIGRvbWFpbl9jYXRlZ29yeSAKICAgICAgICAgICAgICBFTkQgQVMgZG9tYWluX2NhdGVnb3J5LAogICAgICAgICAgICAgIHZhbHVlCiAgICAgICBGUk9NIHN1YgogICAgICAiCgphZ2NlbnN1c19kZiA8LSB3aXRoaW4oZGJHZXRRdWVyeShjb25uLCBzcWwpLAogICAgICAgICAgICAgICAgICAgICAgZG9tYWluX2NhdGVnb3J5IDwtIGZhY3Rvcihkb21haW5fY2F0ZWdvcnksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkZBUk0gU0FMRVM6IFxuKExFU1NcbiBUSEFOIDIsNTAwICQpIiwgIkZBUk0gU0FMRVM6IFxuKDIsNTAwIFRPXG4gNCw5OTkgJCkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRkFSTSBTQUxFUzogXG4oNSwwMDAgVE9cbiA5LDk5OSAkKSIsICJGQVJNIFNBTEVTOiBcbigxMCwwMDAgVE9cbiAyNCw5OTkgJCkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGQVJNIFNBTEVTOiBcbigyNSwwMDAgVE9cbiA0OSw5OTkgJCkiLCAiRkFSTSBTQUxFUzogXG4oNTAsMDAwIFRPXG4gOTksOTk5ICQpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRkFSTSBTQUxFUzogXG4oMTAwLDAwMCBUT1xuIDQ5OSw5OTkgJCkiLCAiRkFSTSBTQUxFUzogXG4oNTAwLDAwMCBPUlxuIE1PUkUgJCkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVE9UQUxcblNBTEVTIiwgIlRPVEFMXG5TQUxFU1xuUEVSIE9QRVJBVElPTiIpKQopCgprYWJsZV9zdHlsaW5nKGthYmxlKHRhaWwoYWdjZW5zdXNfZGYpKSwKICAgICAgICAgICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKYGBgCgpgYGB7ciBmaWc1LCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTcsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpwbG90X21hdCA8LSB3aXRoKGFnY2Vuc3VzX2RmW2FnY2Vuc3VzX2RmJGRvbWFpbl9jYXRlZ29yeSAhPSAnVE9UQUxcblNBTEVTJyxdLCAKICAgICAgICAgICAgICAgICB0YXBwbHkodmFsdWUsIGxpc3QoeWVhciwgZmFjdG9yKGRvbWFpbl9jYXRlZ29yeSkpLCBzdW0pKQoKcGFyKG1hcj1jKDUsIDUsIDIsIDEpKQpiYXJwbG90KHBsb3RfbWF0LCBtYWluPSJVLlMuIEFncmljdWx0dXJlIENlbnN1czogQ29tbW9kaXR5IFRvdGFscyAtIEZhcm0gU2FsZXMiLCBjZXgubWFpbj0xLjUsCiAgICAgICAgY29sPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCB5bGltPWMoMCwgMUU2KSwgYmVzaWRlPVRSVUUsIHhheHQ9Im4iLCB5YXh0PSJuIikKCmF4aXMoc2lkZT0xLCBhdD1jKDMsOSwxNSwyMSwyNywzMywzOSw0NSw1MSkrMC41LCBsYWJlbHM9Y29sbmFtZXMocGxvdF9tYXQpLCB0aWNrPUZBTFNFLCBwYWRqPTAuOSwgcG9zPWMoMCwwKSkKYXhpcyhzaWRlPTIsIGF0PWF4VGlja3MoMiksIGxhYmVscz1mb3JtYXQoYXhUaWNrcygyKSwgYmlnLm1hcms9JywnLCBzY2llbnRpZmljPUZBTFNFKSwgbGFzPTEpCmxlZ2VuZCgidG9wIiwgcm93Lm5hbWVzKHBsb3RfbWF0KSwgZmlsbD1zZWFib3JuX3BhbGV0dGVbMTo1XSwgbmNvbD01KQpgYGAKCgpgYGB7ciBmaWc2LCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpwbG90X21hdCA8LSB3aXRoKGFnY2Vuc3VzX2RmW2FnY2Vuc3VzX2RmJGRvbWFpbl9jYXRlZ29yeSA9PSAnVE9UQUxcblNBTEVTJyxdLCAKICAgICAgICAgICAgICAgICB0YXBwbHkodmFsdWUsIGxpc3QoeWVhciwgZmFjdG9yKGRvbWFpbl9jYXRlZ29yeSkpLCBzdW0pKQoKcGFyKG1hcj1jKDUsIDgsIDIsIDEpKQpiYXJwbG90KHBsb3RfbWF0LCBtYWluPSJVLlMuIEFncmljdWx0dXJlIENlbnN1czogQ29tbW9kaXR5IFRvdGFscyAtIE92ZXJhbGwgRmFybSBTYWxlcyIsIGNleC5tYWluPTEuNSwKICAgICAgICBjb2w9c2VhYm9ybl9wYWxldHRlWzE6NV0sIHlsaW09YygwLCA0RTExKzVFMTApLCBiZXNpZGU9VFJVRSwgeGF4dD0ibiIsIHlheHQ9Im4iLCBzcGFjZT0yKQogICAgICAgIApheGlzKHNpZGU9MSwgYXQ9YygyLjUsNS41LDguNSwxMS41LDE0LjUpLCBsYWJlbHM9cm93Lm5hbWVzKHBsb3RfbWF0KSwgcGFkaj0wLjksIHBvcz1jKDAsMCkpCmF4aXMoc2lkZT0yLCBhdD1heFRpY2tzKDIpLCBsYWJlbHM9Zm9ybWF0KGF4VGlja3MoMiksIGJpZy5tYXJrPScsJywgc2NpZW50aWZpYz1GQUxTRSksIGxhcz0xKQpsZWdlbmQoInRvcCIsIHJvdy5uYW1lcyhwbG90X21hdCksIGZpbGw9c2VhYm9ybl9wYWxldHRlWzE6NV0sIG5jb2w9NSkKYGBgCgpgYGB7cn0Kc3FsIDwtICJTRUxFQ1QgeWVhciwgCiAgICAgICAgICAgICAgIFJFUExBQ0UoUkVQTEFDRShkYXRhX2l0ZW0sICcgLSBPUEVSQVRJT05TIFdJVEggQVJFQSBIQVJWRVNURUQnLCAnJyksICc7IEdSQUlOJywgJycpIGFzIGNyb3AsCiAgICAgICAgICAgICAgIHZhbHVlCiAgICAgICAgRlJPTSBhZ19jZW5zdXMKICAgICAgICBXSEVSRSBkYXRhX2l0ZW0gSU4gKCdDT1JOOyBHUkFJTiAtIE9QRVJBVElPTlMgV0lUSCBBUkVBIEhBUlZFU1RFRCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnV0hFQVQgLSBPUEVSQVRJT05TIFdJVEggQVJFQSBIQVJWRVNURUQnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NPWUJFQU5TIC0gT1BFUkFUSU9OUyBXSVRIIEFSRUEgSEFSVkVTVEVEJykKICAgICAgICBPUkRFUiBCWSB5ZWFyLCBkYXRhX2l0ZW0KICAgICAgICIKCmNyb3BzX2RmIDwtIGRiR2V0UXVlcnkoY29ubiwgc3FsKQoKa2FibGVfc3R5bGluZyhrYWJsZSh0YWlsKGNyb3BzX2RmKSksCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKYGBge3IgZmlnNywgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDE1LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KcGxvdF9tYXQgPC0gd2l0aChjcm9wc19kZiwgdGFwcGx5KHZhbHVlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QoeWVhciwgZmFjdG9yKGNyb3AsIGxldmVscz1jKCJXSEVBVCIsICJTT1lCRUFOUyIsICJDT1JOIikpKSwgc3VtKQopCgpwYXIobWFyPWMoNSwgOCwgMiwgMSkpCmJhcnBsb3QocGxvdF9tYXQsIG1haW49IlUuUy4gQWdyaWN1bHR1cmUgQ2Vuc3VzOiBOdW1iZXIgb2YgT3BlcmF0aW9ucyBieSBTcGVjaWZpYyBDcm9wcyIsIGNleC5tYWluPTEuNSwKICAgICAgICBjb2w9c2VhYm9ybl9wYWxldHRlWzE6NV0sIHlsaW09YygwLCA1RTUpLCB5YXh0PSJuIiwgYmVzaWRlPVRSVUUpCgpheGlzKHNpZGU9MiwgYXQ9YXhUaWNrcygyKSwgbGFiZWxzPWZvcm1hdChheFRpY2tzKDIpLCBiaWcubWFyaz0nLCcsIHNjaWVudGlmaWM9RkFMU0UpLCBsYXM9MSkKbGVnZW5kKCJ0b3AiLCByb3cubmFtZXMocGxvdF9tYXQpLCBmaWxsPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCBuY29sPTUpCmBgYAoKYGBge3J9CnNxbCA8LSAiU0VMRUNUIHllYXIsIAogICAgICAgICAgICAgICBSRVBMQUNFKAogICAgICAgICAgICAgICAgICBSRVBMQUNFKAogICAgICAgICAgICAgICAgICAgICBSRVBMQUNFKGRhdGFfaXRlbSwgJyAtIE9QRVJBVElPTlMgV0lUSCBTQUxFUycsICcnKSwgCiAgICAgICAgICAgICAgICAgICAgICc7IElOQ0wgQ0FMVkVTJywgJycpLAogICAgICAgICAgICAgICAgICAnOyBCUk9JTEVSUycsICcnKSBhcyBsaXZlc3RvY2ssCiAgICAgICAgICAgICAgIHZhbHVlCiAgICAgICAgRlJPTSBhZ19jZW5zdXMKICAgICAgICBXSEVSRSBkYXRhX2l0ZW0gSU4gKCdDQVRUTEU7IElOQ0wgQ0FMVkVTIC0gT1BFUkFUSU9OUyBXSVRIIFNBTEVTJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICdIT0dTIC0gT1BFUkFUSU9OUyBXSVRIIFNBTEVTJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICdDSElDS0VOUzsgQlJPSUxFUlMgLSBPUEVSQVRJT05TIFdJVEggU0FMRVMnKQogICAgICAgIE9SREVSIEJZIHllYXIsIGRhdGFfaXRlbQogICAgICAgIgoKbGl2ZXN0b2NrX2RmIDwtIGRiR2V0UXVlcnkoY29ubiwgc3FsKQoKa2FibGVfc3R5bGluZyhrYWJsZSh0YWlsKGxpdmVzdG9ja19kZikpLAogICAgICAgICAgICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCgpgYGB7ciBmaWc4LCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpwbG90X21hdCA8LSB3aXRoKGxpdmVzdG9ja19kZiwgdGFwcGx5KHZhbHVlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QoeWVhciwgZmFjdG9yKGxpdmVzdG9jaywgbGV2ZWxzPWMoICJDSElDS0VOUyIsICJIT0dTIiwgIkNBVFRMRSIpKSksIHN1bSkKKQoKcGFyKG1hcj1jKDUsIDgsIDIsIDEpKQpiYXJwbG90KHBsb3RfbWF0LCBtYWluPSJVLlMuIEFncmljdWx0dXJlIENlbnN1czogTnVtYmVyIG9mIE9wZXJhdGlvbnMgYnkgU3BlY2lmaWMgTGl2ZXN0b2NrIiwgY2V4Lm1haW49MS41LAogICAgICAgIGNvbD1zZWFib3JuX3BhbGV0dGVbMTo1XSwgeWxpbT1jKDAsIDFFNisyRTUpLCB5YXh0PSJuIiwgYmVzaWRlPVRSVUUpCgpheGlzKHNpZGU9MiwgYXQ9YXhUaWNrcygyKSwgbGFiZWxzPWZvcm1hdChheFRpY2tzKDIpLCBiaWcubWFyaz0nLCcsIHNjaWVudGlmaWM9RkFMU0UpLCBsYXM9MSkKbGVnZW5kKCJ0b3AiLCByb3cubmFtZXMocGxvdF9tYXQpLCBmaWxsPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCBuY29sPTUpCmBgYAoKCmBgYHtyfQpzcWwgPC0gIlNFTEVDVCB5ZWFyLCAKICAgICAgICAgICAgICAgQ0FTRQogICAgICAgICAgICAgICAgICAgV0hFTiBkYXRhX2l0ZW0gPSAnQ1JPUCBUT1RBTFMgLSBTQUxFUzsgTUVBU1VSRUQgSU4gJCcgVEhFTiAnQ1JPUCBUT1RBTFMnCiAgICAgICAgICAgICAgICAgICBXSEVOIGRhdGFfaXRlbSA9ICdBTklNQUwgVE9UQUxTOyBJTkNMIFBST0RVQ1RTIC0gU0FMRVM7IE1FQVNVUkVEIElOICQnIFRIRU4gJ0FOSU1BTCBUT1RBTFMnCiAgICAgICAgICAgICAgIEVORCBhcyBjYXRlZ29yeSwKICAgICAgICAgICAgICAgdmFsdWUKICAgICAgICBGUk9NIGFnX2NlbnN1cwogICAgICAgIFdIRVJFIGRhdGFfaXRlbSBJTiAoJ0NST1AgVE9UQUxTIC0gU0FMRVM7IE1FQVNVUkVEIElOICQnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0FOSU1BTCBUT1RBTFM7IElOQ0wgUFJPRFVDVFMgLSBTQUxFUzsgTUVBU1VSRUQgSU4gJCcpCiAgICAgICAgT1JERVIgQlkgeWVhciwgZGF0YV9pdGVtCiAgICAgICAiCgphZ3RvdGFsX2RmIDwtIGRiR2V0UXVlcnkoY29ubiwgc3FsKQoKa2FibGVfc3R5bGluZyhrYWJsZSh0YWlsKGFndG90YWxfZGYpKSwKICAgICAgICAgICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKYGBgCgpgYGB7ciBmaWc5LCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpwbG90X21hdCA8LSB3aXRoKGFndG90YWxfZGYsIHRhcHBseSh2YWx1ZSwgbGlzdCh5ZWFyLCBjYXRlZ29yeSksIHN1bSkpCgpwYXIobWFyPWMoNSwgOCwgMiwgMSkpCmJhcnBsb3QocGxvdF9tYXQsIG1haW49IlUuUy4gQWdyaWN1bHR1cmUgQ2Vuc3VzOiBDcm9wIHZzIEFuaW1hbCBTYWxlcyIsIGNleC5tYWluPTEuNSwKICAgICAgICBjb2w9c2VhYm9ybl9wYWxldHRlWzE6NV0sIHlsaW09YygwLCAyRTExKzVFMTApLCB5YXh0PSJuIiwgYmVzaWRlPVRSVUUpCgpheGlzKHNpZGU9MiwgYXQ9YXhUaWNrcygyKSwgbGFiZWxzPWZvcm1hdChheFRpY2tzKDIpLCBiaWcubWFyaz0nLCcsIHNjaWVudGlmaWM9RkFMU0UpLCBsYXM9MSkKbGVnZW5kKCJ0b3AiLCByb3cubmFtZXMocGxvdF9tYXQpLCBmaWxsPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCBuY29sPTUpCmBgYAoKPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQiPjxpbWcgc3JjPSJJTUFHRVMvd29ybGRfbWV0cmljcy5wbmciIHdpZHRoPSIzMDBweCIvPjwvZGl2PgojIyMgV29ybGQgTWV0cmljcwoKYGBge3J9CnNxbCA8LSAiV0lUSCBwb3AgQVMgCiAgICAgICAgICAgIChTRUxFQ1QgcC55ZWFyLAogICAgICAgICAgICAgICAgICAgIHAucG9wdWxhdGlvbgogICAgICAgICAgICAgRlJPTSB3b3JsZF9wb3B1bGF0aW9uIHAKICAgICAgICAgICAgIFdIRVJFIHAuY291bnRyeV9uYW1lID0gJ1dvcmxkJwogICAgICAgICAgICAgICBBTkQgcC55ZWFyIEJFVFdFRU4gMjAwMCBBTkQgMjAxOQogICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAKICAgICAgICAgICAgICBsYW5kIEFTCiAgICAgICAgICAgIChTRUxFQ1QgYS55ZWFyLAogICAgICAgICAgICAgICAgICAgIGEucGVyY2VudF9hcmFibGUKICAgICAgICAgICAgIEZST00gYXJhYmxlX2xhbmQgYQogICAgICAgICAgICAgV0hFUkUgYS5jb3VudHJ5X25hbWUgPSAnV29ybGQnCiAgICAgICAgICAgICAgIEFORCBhLnllYXIgQkVUV0VFTiAyMDAwIEFORCAyMDE5CiAgICAgICAgICAgICApLAogICAgICAgICAKICAgICAgICAgICAgICBmYXVuYSBBUwogICAgICAgICAgICAoU0VMRUNUIGkueWVhciwKICAgICAgICAgICAgICAgICAgICBTVU0oaS5zcGVjaWVzX2NvdW50KSBBUyBhbmltYWxzX2NvdW50CiAgICAgICAgICAgICBGUk9NIGl1Y25fc3BlY2llc19jb3VudCBpCiAgICAgICAgICAgICBXSEVSRSBpLnllYXIgQkVUV0VFTiAyMDAwIEFORCAyMDE5CiAgICAgICAgICAgICBHUk9VUCBCWSBpLnllYXIKICAgICAgICAgICAgICksCiAgICAgICAgIAogICAgICAgICAgICAgIGZsb3JhIEFTCiAgICAgICAgICAgIChTRUxFQ1QgcC5hc3Nlc3NtZW50X3llYXIgYXMgeWVhciwKICAgICAgICAgICAgICAgICAgICBDT1VOVCgqKSBBUyBwbGFudHNfY291bnQKICAgICAgICAgICAgIEZST00gcGxhbnRzX2Fzc2Vzc21lbnRzIHAKICAgICAgICAgICAgIFdIRVJFIHAuYXNzZXNzbWVudF95ZWFyIEJFVFdFRU4gMjAwMCBBTkQgMjAxOQogICAgICAgICAgICAgICBBTkQgcC5pbnRlcnByZXRlZF9zdGF0dXMgPSAnVGhyZWF0ZW5lZCcKICAgICAgICAgICAgIEdST1VQIEJZIHAuYXNzZXNzbWVudF95ZWFyCiAgICAgICAgICAgICApLCAgICAgICAgICAgICAKICAgICAgICAgCiAgICAgICAgICAgICAgaWNlIEFTCiAgICAgICAgICAgIChTRUxFQ1Qgcy5kYXRlX3llYXIgYXMgeWVhciwKICAgICAgICAgICAgICAgICAgICBBVkcocy5leHRlbnQpIEZJTFRFUihXSEVSRSBzLnJlZ2lvbiA9ICdBcmN0aWMnKSBBUyBhcmN0aWNfc2VhX2ljZV9leHRlbnQsCiAgICAgICAgICAgICAgICAgICAgQVZHKHMuZXh0ZW50KSBGSUxURVIoV0hFUkUgcy5yZWdpb24gPSAnQW50YXJjdGljYScpIEFTIGFudGFyY3RpY19zZWFfaWNlX2V4dGVudAogICAgICAgICAgICAgRlJPTSBzZWFfaWNlX2V4dGVudCBzCiAgICAgICAgICAgICBXSEVSRSBzLmRhdGVfeWVhciBCRVRXRUVOIDIwMDAgQU5EIDIwMTkKICAgICAgICAgICAgIEdST1VQIEJZIHMuZGF0ZV95ZWFyCiAgICAgICAgICAgICApLAogICAgICAgICAgICAgCiAgICAgICAgICAgICAgb2NlYW4gQVMKICAgICAgICAgICAgKFNFTEVDVCBvLnllYXIgYXMgeWVhciwKICAgICAgICAgICAgICAgICAgICBBVkcoby50Y28yKSBBUyB0b3RhbF9jbzIsCiAgICAgICAgICAgICAgICAgICAgQVZHKG8ucGh0czI1cDApIEFTIHBoX3NjYWxlCiAgICAgICAgICAgICBGUk9NIG9jZWFuX2RhdGEgbwogICAgICAgICAgICAgV0hFUkUgby55ZWFyIEJFVFdFRU4gMjAwMCBBTkQgMjAxOQogICAgICAgICAgICAgICBBTkQgby50Y28yIDw+IC05OTk5IEFORCBvLnBodHMyNXAwIDw+IC05OTk5CiAgICAgICAgICAgICBHUk9VUCBCWSBvLnllYXIKICAgICAgICAgICAgICksCiAgICAgICAgICAgICAKICAgICAgICAgICAgICB0ZW1wIEFTCiAgICAgICAgICAgIChTRUxFQ1QgZy55ZWFyIGFzIHllYXIsCiAgICAgICAgICAgICAgICAgICAgQVZHKGcuZ2xvYmFsX21lYW4pIEFTIGdsb2JhbF9tZWFuCiAgICAgICAgICAgICBGUk9NIGdsb2JhbF90ZW1wZXJhdHVyZSBnCiAgICAgICAgICAgICBXSEVSRSBnLnllYXIgQkVUV0VFTiAyMDAwIEFORCAyMDE5CiAgICAgICAgICAgICBHUk9VUCBCWSBnLnllYXIKICAgICAgICAgICAgICkKICAgICAgICAgICAgIAogICAgICAgICBTRUxFQ1QgcG9wLnllYXIsIHBvcC5wb3B1bGF0aW9uLCBsYW5kLnBlcmNlbnRfYXJhYmxlLCBmYXVuYS5hbmltYWxzX2NvdW50LCBmbG9yYS5wbGFudHNfY291bnQsCiAgICAgICAgICAgICAgICBpY2UuYXJjdGljX3NlYV9pY2VfZXh0ZW50LCBpY2UuYW50YXJjdGljX3NlYV9pY2VfZXh0ZW50LCAKICAgICAgICAgICAgICAgIG9jZWFuLnRvdGFsX2NvMiwgb2NlYW4ucGhfc2NhbGUsIHRlbXAuZ2xvYmFsX21lYW4KICAgICAgICAgRlJPTSBwb3AgCiAgICAgICAgIEpPSU4gbGFuZCBVU0lORyAoeWVhcikKICAgICAgICAgSk9JTiBmYXVuYSBVU0lORyAoeWVhcikKICAgICAgICAgSk9JTiBmbG9yYSBVU0lORyAoeWVhcikKICAgICAgICAgSk9JTiBpY2UgVVNJTkcgKHllYXIpCiAgICAgICAgIEpPSU4gb2NlYW4gVVNJTkcgKHllYXIpCiAgICAgICAgIEpPSU4gdGVtcCBVU0lORyAoeWVhcikKICAgICAgICAgT1JERVIgQlkgcG9wLnllYXIiCgplbnZfd29ybGRfZGYgPC0gZGJHZXRRdWVyeShjb25uLCBzcWwpCgprYWJsZV9zdHlsaW5nKGthYmxlKHRhaWwoZW52X3dvcmxkX2RmKSksIGZvbnRfc2l6ZSA9IDEyLAogICAgICAgICAgICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCmBgYHtyfQprYWJsZV9zdHlsaW5nKGthYmxlKGNvcihlbnZfd29ybGRfZGZbLTFdKSksIGZvbnRfc2l6ZSA9IDEyLAogICAgICAgICAgICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCmBgYHtyIGZpZzEwLCBmaWcuaGVpZ2h0ID0gMTUsIGZpZy53aWR0aCA9IDE1LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KcGFyKG1mcm93PWMoNCwyKSwgbWFpID0gYygwLjMsIDAuMiwgMC43LCAwLjIpKQoKZm9yKHggaW4gY29sbmFtZXMoZW52X3dvcmxkX2RmKVszOm5jb2woZW52X3dvcmxkX2RmKV0pIHsKICBsZml0IDwtIGxvZXNzKHBhc3RlKHgsICJ+IHBvcHVsYXRpb24iKSwgZGF0YT1lbnZfd29ybGRfZGYpCiAgCiAgcGxvdChhcy5mb3JtdWxhKHBhc3RlKHgsICJ+IHBvcHVsYXRpb24iKSksIGVudl93b3JsZF9kZiwgCiAgICAgICBtYWluPXBhc3RlKCJwb3B1bGF0aW9uIGFuZFxuIiwgZ3N1YigiXyIsICIgIiwgeCkpLCBjZXgubWFpbj0yLAogICAgICAgdHlwZT0icCIsIGNvbD1zZWFib3JuX3BhbGV0dGVbMV0sIHlheHQ9J24nLCB4YXh0PSduJywgcGNoPTE5KQogIGF4aXMoc2lkZT0xLCBhdD1heFRpY2tzKDEpLCBsYWJlbHM9Zm9ybWF0KGF4VGlja3MoMSksIGJpZy5tYXJrPScsJywgc2NpZW50aWZpYz1GQUxTRSkpCiAgYXhpcyhzaWRlPTIsIGF0PWF4VGlja3MoMiksIGxhYmVscz1mb3JtYXQoYXhUaWNrcygyKSwgYmlnLm1hcms9JywnLCBzY2llbnRpZmljPUZBTFNFKSkKICBwb3Bfb3JkZXIgPC0gb3JkZXIoZW52X3dvcmxkX2RmJHBvcHVsYXRpb24pCiAgbGluZXMoZW52X3dvcmxkX2RmJHBvcHVsYXRpb25bcG9wX29yZGVyXSwgbGZpdCRmaXR0ZWRbcG9wX29yZGVyXSwgY29sPXNlYWJvcm5fcGFsZXR0ZVs0XSwgbHdkPTMpCn0KCmBgYAoKCmBgYHtyfQpkYkRpc2Nvbm5lY3QoY29ubikKYGBgCgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGltZyBzcmM9IklNQUdFUy9yX3NoaW55LnBuZyIvPjwvZGl2PgoKIyMjIFVzZXIgRGF0YSBBcHBsaWNhdGlvbgoKPGRpdiBzdHlsZT0iZmxvYXQ6Y2VudGVyIj48aW1nIHNyYz0iSU1BR0VTL2Vudl9kYXRhLnBuZyIgd2lkdGg9IjQwMHB4Ii8+PC9kaXY+CgpgYGB7cn0KCiMgTElOVVggU0hFTEwgQ09NTUFORCBDQUxMCnN5c3RlbShwYXN0ZTAoImdub21lLXRlcm1pbmFsIC0tIFJzY3JpcHQgLWUgXCJsaWJyYXJ5KHNoaW55KTsgc2V0d2QoJyIsIGdldHdkKCksICInKTsgcnVuQXBwKCdFbnZpcm9ubWVudERCX1NoaW55X0FwcC5SJylcIiIpKQoKYGBgCgo8aHIgc3R5bGU9ImJvcmRlcjogbm9uZTsgaGVpZ2h0OiAxcHg7IGJhY2tncm91bmQtY29sb3I6ICNDQ0M7Ii8+Cgo8aDI+PHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+Q29uY2x1c2lvbjwvc3Bhbj48L2gyPgoKPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQ7Ij48aW1nIHNyYz0iSU1BR0VTL2RhdGFfcGlwZWxpbmUucG5nIiB3aWR0aD0iMzUwcHgiLz48L2Rpdj4KPGJyLz4KCjxkaXYgc3R5bGU9ImZsb2F0OmxlZnQ7Ij48aW1nIHNyYz0iSU1BR0VTL3Bvc3RncmVzcWxfci5wbmciIHdpZHRoPSIxMDBweCIvPjwvZGl2Pgo8YnIvPgo8YnIvPgoKLSA8aDM+PHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+UG9zdGdyZXMgYXMgdG9vbCBpbiBkYXRhIHNjaWVuY2Ugd29ya2Zsb3c8L2gzPgotIDxoMz48c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5EYXRhIHBlcnNpc3RlbmNlIGFuZCBoeWdpZW5lPC9oMz4KLSA8aDM+PHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+Q2VudHJhbGl6YXRpb24gYW5kIHNlY3VyaXR5PC9oMz4KLSA8aDM+PHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+RWZmaWNpZW5jeSBhbmQgdXNlZnVsbmVzczwvaDM+Ci0gPGgzPjxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPlNjYWxhYmlsaXR5IGFuZCBhcHBsaWNhYmlsaXR5PC9oMz4KCjxici8+Cjxici8+CgoK